blob: ad629c575155f115a009f77e6ca193f6bfa361f9 [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"
Brian Salomonb8f098d2020-01-07 11:15:44 -050015#include "src/gpu/effects/GrTextureEffect.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050016#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
Brian Salomon7caa1372019-12-09 10:40:56 -050058static void append_wrap(GrGLSLShaderBuilder* builder, GrTextureDomain::Mode mode,
59 const char* inCoord, const char* domainStart, const char* domainEnd,
Brian Salomon095d2462019-12-09 15:22:33 -050060 bool is2D, const char* out) {
Michael Ludwigbe315a22018-12-17 09:50:51 -050061 switch(mode) {
62 case GrTextureDomain::kIgnore_Mode:
Brian Salomon7caa1372019-12-09 10:40:56 -050063 builder->codeAppendf("%s = %s;\n", out, inCoord);
Michael Ludwigbe315a22018-12-17 09:50:51 -050064 break;
65 case GrTextureDomain::kDecal_Mode:
66 // The lookup coordinate to use for decal will be clamped just like kClamp_Mode,
67 // it's just that the post-processing will be different, so fall through
68 case GrTextureDomain::kClamp_Mode:
Brian Salomon7caa1372019-12-09 10:40:56 -050069 builder->codeAppendf("%s = clamp(%s, %s, %s);", out, inCoord, domainStart, domainEnd);
Michael Ludwigbe315a22018-12-17 09:50:51 -050070 break;
71 case GrTextureDomain::kRepeat_Mode:
Brian Salomon7caa1372019-12-09 10:40:56 -050072 builder->codeAppendf("%s = mod(%s - %s, %s - %s) + %s;", out, inCoord, domainStart,
73 domainEnd, domainStart, domainStart);
Michael Ludwigbe315a22018-12-17 09:50:51 -050074 break;
Brian Salomon7caa1372019-12-09 10:40:56 -050075 case GrTextureDomain::kMirrorRepeat_Mode: {
Brian Salomon095d2462019-12-09 15:22:33 -050076 const char* type = is2D ? "float2" : "float";
Brian Salomon7caa1372019-12-09 10:40:56 -050077 builder->codeAppend("{");
Brian Salomon095d2462019-12-09 15:22:33 -050078 builder->codeAppendf("%s w = %s - %s;", type, domainEnd, domainStart);
79 builder->codeAppendf("%s w2 = 2 * w;", type);
80 builder->codeAppendf("%s m = mod(%s - %s, w2);", type, inCoord, domainStart);
Brian Salomon7caa1372019-12-09 10:40:56 -050081 builder->codeAppendf("%s = mix(m, w2 - m, step(w, m)) + %s;", out, domainStart);
82 builder->codeAppend("}");
Michael Ludwigbe315a22018-12-17 09:50:51 -050083 break;
Brian Salomon7caa1372019-12-09 10:40:56 -050084 }
Michael Ludwigbe315a22018-12-17 09:50:51 -050085 }
Michael Ludwigbe315a22018-12-17 09:50:51 -050086}
87
Brian Salomon7eabfe82019-12-02 14:20:20 -050088void GrTextureDomain::GLDomain::sampleProcessor(const GrTextureDomain& textureDomain,
89 const char* inColor,
90 const char* outColor,
91 const SkString& inCoords,
92 GrGLSLFragmentProcessor* parent,
93 GrGLSLFragmentProcessor::EmitArgs& args,
94 int childIndex) {
95 auto appendProcessorSample = [parent, &args, childIndex, inColor](const char* coord) {
96 SkString outColor("childColor");
97 parent->invokeChild(childIndex, inColor, &outColor, args, coord);
98 return outColor;
99 };
100 this->sample(args.fFragBuilder, args.fUniformHandler, textureDomain, outColor, inCoords,
101 appendProcessorSample);
102}
103
egdaniel2d721d32015-11-11 13:06:05 -0800104void GrTextureDomain::GLDomain::sampleTexture(GrGLSLShaderBuilder* builder,
egdaniel7ea439b2015-12-03 09:20:44 -0800105 GrGLSLUniformHandler* uniformHandler,
Brian Salomon1edc5b92016-11-29 13:43:46 -0500106 const GrShaderCaps* shaderCaps,
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000107 const GrTextureDomain& textureDomain,
108 const char* outColor,
109 const SkString& inCoords,
egdaniel09aa1fc2016-04-20 07:09:46 -0700110 GrGLSLFragmentProcessor::SamplerHandle sampler,
Brian Osman2240be92017-10-18 13:15:13 -0400111 const char* inModulateColor) {
Brian Salomon7eabfe82019-12-02 14:20:20 -0500112 auto appendTextureSample = [&sampler, inModulateColor, builder](const char* coord) {
113 builder->codeAppend("half4 textureColor = ");
Brian Salomon87e9ddb2019-12-19 14:50:22 -0500114 builder->appendTextureLookupAndBlend(inModulateColor, SkBlendMode::kModulate, sampler,
115 coord);
Brian Salomon7eabfe82019-12-02 14:20:20 -0500116 builder->codeAppend(";");
117 return SkString("textureColor");
118 };
119 this->sample(builder, uniformHandler, textureDomain, outColor, inCoords, appendTextureSample);
120}
121
122void GrTextureDomain::GLDomain::sample(GrGLSLShaderBuilder* builder,
123 GrGLSLUniformHandler* uniformHandler,
124 const GrTextureDomain& textureDomain,
125 const char* outColor,
126 const SkString& inCoords,
127 const std::function<AppendSample>& appendSample) {
Michael Ludwigbe315a22018-12-17 09:50:51 -0500128 SkASSERT(!fHasMode || (textureDomain.modeX() == fModeX && textureDomain.modeY() == fModeY));
129 SkDEBUGCODE(fModeX = textureDomain.modeX();)
130 SkDEBUGCODE(fModeY = textureDomain.modeY();)
Hans Wennborgc63ec5c2017-12-08 18:56:23 -0800131 SkDEBUGCODE(fHasMode = true;)
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000132
Michael Ludwigbe315a22018-12-17 09:50:51 -0500133 if ((textureDomain.modeX() != kIgnore_Mode || textureDomain.modeY() != kIgnore_Mode) &&
134 !fDomainUni.isValid()) {
135 // Must include the domain uniform since at least one axis uses it
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000136 const char* name;
137 SkString uniName("TexDom");
138 if (textureDomain.fIndex >= 0) {
139 uniName.appendS32(textureDomain.fIndex);
140 }
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400141 fDomainUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType,
egdaniel7ea439b2015-12-03 09:20:44 -0800142 uniName.c_str(), &name);
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000143 fDomainName = name;
144 }
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000145
Michael Ludwigbe315a22018-12-17 09:50:51 -0500146 bool decalX = textureDomain.modeX() == kDecal_Mode;
147 bool decalY = textureDomain.modeY() == kDecal_Mode;
148 if ((decalX || decalY) && !fDecalUni.isValid()) {
149 const char* name;
150 SkString uniName("DecalParams");
151 if (textureDomain.fIndex >= 0) {
152 uniName.appendS32(textureDomain.fIndex);
joshualitt5ae5fc52014-07-29 12:59:27 -0700153 }
Michael Ludwigbe315a22018-12-17 09:50:51 -0500154 // Half3 since this will hold texture width, height, and then a step function control param
155 fDecalUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf3_GrSLType,
156 uniName.c_str(), &name);
157 fDecalName = name;
158 }
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000159
Michael Ludwigbe315a22018-12-17 09:50:51 -0500160 // Add a block so that we can declare variables
161 GrGLSLShaderBuilder::ShaderBlock block(builder);
162 // Always use a local variable for the input coordinates; often callers pass in an expression
163 // and we want to cache it across all of its references in the code below
164 builder->codeAppendf("float2 origCoord = %s;", inCoords.c_str());
Brian Salomon7caa1372019-12-09 10:40:56 -0500165 builder->codeAppend("float2 clampedCoord;");
166 SkString start;
167 SkString end;
Brian Salomon095d2462019-12-09 15:22:33 -0500168 bool is2D = textureDomain.modeX() == textureDomain.modeY();
169 if (is2D) {
170 // Doing the domain setup using vectors seems to avoid shader compilation issues on
171 // Chromecast, possibly due to reducing shader length.
172 start.printf("%s.xy", fDomainName.c_str());
173 end.printf("%s.zw", fDomainName.c_str());
174 append_wrap(builder, textureDomain.modeX(), "origCoord", start.c_str(), end.c_str(),
175 true, "clampedCoord");
176 } else {
177 // Apply x mode to the x coordinate using the left and right edges of the domain rect
178 // (stored as the x and z components of the domain uniform).
179 start.printf("%s.x", fDomainName.c_str());
180 end.printf("%s.z", fDomainName.c_str());
181 append_wrap(builder, textureDomain.modeX(), "origCoord.x", start.c_str(), end.c_str(),
182 false, "clampedCoord.x");
183 // Repeat the same logic for y.
184 start.printf("%s.y", fDomainName.c_str());
185 end.printf("%s.w", fDomainName.c_str());
186 append_wrap(builder, textureDomain.modeY(), "origCoord.y", start.c_str(), end.c_str(),
187 false, "clampedCoord.y");
188 }
Brian Salomon7eabfe82019-12-02 14:20:20 -0500189 // Sample 'appendSample' at the clamped coordinate location.
190 SkString color = appendSample("clampedCoord");
Michael Ludwigbe315a22018-12-17 09:50:51 -0500191
192 // Apply decal mode's transparency interpolation if needed
193 if (decalX || decalY) {
194 // The decal err is the max absoluate value between the clamped coordinate and the original
195 // pixel coordinate. This will then be clamped to 1.f if it's greater than the control
196 // parameter, which simulates kNearest and kBilerp behavior depending on if it's 0 or 1.
197 if (decalX && decalY) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500198 builder->codeAppendf("half err = max(half(abs(clampedCoord.x - origCoord.x) * %s.x), "
199 "half(abs(clampedCoord.y - origCoord.y) * %s.y));",
Michael Ludwigbe315a22018-12-17 09:50:51 -0500200 fDecalName.c_str(), fDecalName.c_str());
201 } else if (decalX) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500202 builder->codeAppendf("half err = half(abs(clampedCoord.x - origCoord.x) * %s.x);",
Michael Ludwigbe315a22018-12-17 09:50:51 -0500203 fDecalName.c_str());
204 } else {
205 SkASSERT(decalY);
Ethan Nicholase1f55022019-02-05 17:17:40 -0500206 builder->codeAppendf("half err = half(abs(clampedCoord.y - origCoord.y) * %s.y);",
Michael Ludwigbe315a22018-12-17 09:50:51 -0500207 fDecalName.c_str());
joshualitt5ae5fc52014-07-29 12:59:27 -0700208 }
joshualitt5ae5fc52014-07-29 12:59:27 -0700209
Michael Ludwigbe315a22018-12-17 09:50:51 -0500210 // Apply a transform to the error rate, which let's us simulate nearest or bilerp filtering
211 // in the same shader. When the texture is nearest filtered, fSizeName.z is set to 1/2 so
212 // this becomes a step function centered at .5 away from the clamped coordinate (but the
213 // domain for decal is inset by .5 so the edge lines up properly). When bilerp, fSizeName.z
214 // is set to 1 and it becomes a simple linear blend between texture and transparent.
215 builder->codeAppendf("if (err > %s.z) { err = 1.0; } else if (%s.z < 1) { err = 0.0; }",
216 fDecalName.c_str(), fDecalName.c_str());
Brian Salomon7eabfe82019-12-02 14:20:20 -0500217 builder->codeAppendf("%s = mix(%s, half4(0, 0, 0, 0), err);", outColor, color.c_str());
Michael Ludwigbe315a22018-12-17 09:50:51 -0500218 } else {
219 // A simple look up
Brian Salomon7eabfe82019-12-02 14:20:20 -0500220 builder->codeAppendf("%s = %s;", outColor, color.c_str());
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000221 }
222}
223
egdaniel018fb622015-10-28 07:26:40 -0700224void GrTextureDomain::GLDomain::setData(const GrGLSLProgramDataManager& pdman,
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000225 const GrTextureDomain& textureDomain,
Robert Phillipsbd99c0c2019-12-12 13:26:58 +0000226 const GrSurfaceProxyView& view,
Brian Salomonccb61422020-01-09 10:46:36 -0500227 GrSamplerState state) {
Brian Salomon7eabfe82019-12-02 14:20:20 -0500228 // We want a hard transition from texture content to trans-black in nearest mode.
229 bool filterDecal = state.filter() != GrSamplerState::Filter::kNearest;
Robert Phillipsbd99c0c2019-12-12 13:26:58 +0000230 this->setData(pdman, textureDomain, view.proxy(), view.origin(), filterDecal);
Brian Salomon7eabfe82019-12-02 14:20:20 -0500231}
Michael Ludwig170de012019-11-15 21:55:18 +0000232
Brian Salomon7eabfe82019-12-02 14:20:20 -0500233void GrTextureDomain::GLDomain::setData(const GrGLSLProgramDataManager& pdman,
234 const GrTextureDomain& textureDomain,
235 bool filterIfDecal) {
Robert Phillipsbd99c0c2019-12-12 13:26:58 +0000236 // The origin we pass here doesn't matter
237 this->setData(pdman, textureDomain, nullptr, kTopLeft_GrSurfaceOrigin, filterIfDecal);
Brian Salomon7eabfe82019-12-02 14:20:20 -0500238}
239
240void GrTextureDomain::GLDomain::setData(const GrGLSLProgramDataManager& pdman,
241 const GrTextureDomain& textureDomain,
242 const GrSurfaceProxy* proxy,
Robert Phillipsbd99c0c2019-12-12 13:26:58 +0000243 GrSurfaceOrigin origin,
Brian Salomon7eabfe82019-12-02 14:20:20 -0500244 bool filterIfDecal) {
245 SkASSERT(fHasMode && textureDomain.modeX() == fModeX && textureDomain.modeY() == fModeY);
246 if (kIgnore_Mode == textureDomain.modeX() && kIgnore_Mode == textureDomain.modeY()) {
247 return;
248 }
249 // If the texture is using nearest filtering, then the decal filter weight should step from
250 // 0 (texture) to 1 (transparent) one half pixel away from the domain. When doing any other
251 // form of filtering, the weight should be 1.0 so that it smoothly interpolates between the
252 // texture and transparent.
253 // Start off assuming we're in pixel units and later adjust if we have to deal with normalized
254 // texture coords.
255 float decalFilterWeights[3] = {1.f, 1.f, filterIfDecal ? 1.f : 0.5f};
256 bool sendDecalData = textureDomain.modeX() == kDecal_Mode ||
257 textureDomain.modeY() == kDecal_Mode;
258 float tempDomainValues[4];
259 const float* values;
260 if (proxy) {
Brian Salomon246bc3d2018-12-06 15:33:02 -0500261 SkScalar wInv, hInv, h;
Brian Salomon7eabfe82019-12-02 14:20:20 -0500262 GrTexture* tex = proxy->peekTexture();
Michael Ludwig8fa469d2019-11-25 16:08:44 -0500263 if (proxy->backendFormat().textureType() == GrTextureType::kRectangle) {
Brian Salomon246bc3d2018-12-06 15:33:02 -0500264 wInv = hInv = 1.f;
265 h = tex->height();
Brian Salomon7eabfe82019-12-02 14:20:20 -0500266 // Don't do any scaling by texture size for decal filter rate, it's already in
267 // pixels
Brian Salomon246bc3d2018-12-06 15:33:02 -0500268 } else {
269 wInv = SK_Scalar1 / tex->width();
270 hInv = SK_Scalar1 / tex->height();
271 h = 1.f;
Michael Ludwigbe315a22018-12-17 09:50:51 -0500272
Brian Salomon7eabfe82019-12-02 14:20:20 -0500273 // Account for texture coord normalization in decal filter weights.
274 decalFilterWeights[0] = tex->width();
275 decalFilterWeights[1] = tex->height();
Brian Salomon246bc3d2018-12-06 15:33:02 -0500276 }
Robert Phillipse98234f2017-01-09 14:23:59 -0500277
Brian Salomon7eabfe82019-12-02 14:20:20 -0500278 tempDomainValues[0] = SkScalarToFloat(textureDomain.domain().fLeft * wInv);
279 tempDomainValues[1] = SkScalarToFloat(textureDomain.domain().fTop * hInv);
280 tempDomainValues[2] = SkScalarToFloat(textureDomain.domain().fRight * wInv);
281 tempDomainValues[3] = SkScalarToFloat(textureDomain.domain().fBottom * hInv);
Robert Phillipse98234f2017-01-09 14:23:59 -0500282
Michael Ludwig8fa469d2019-11-25 16:08:44 -0500283 if (proxy->backendFormat().textureType() == GrTextureType::kRectangle) {
Brian Salomon7eabfe82019-12-02 14:20:20 -0500284 SkASSERT(tempDomainValues[0] >= 0.0f && tempDomainValues[0] <= proxy->width());
285 SkASSERT(tempDomainValues[1] >= 0.0f && tempDomainValues[1] <= proxy->height());
286 SkASSERT(tempDomainValues[2] >= 0.0f && tempDomainValues[2] <= proxy->width());
287 SkASSERT(tempDomainValues[3] >= 0.0f && tempDomainValues[3] <= proxy->height());
Brian Salomon246bc3d2018-12-06 15:33:02 -0500288 } else {
Brian Salomon7eabfe82019-12-02 14:20:20 -0500289 SkASSERT(tempDomainValues[0] >= 0.0f && tempDomainValues[0] <= 1.0f);
290 SkASSERT(tempDomainValues[1] >= 0.0f && tempDomainValues[1] <= 1.0f);
291 SkASSERT(tempDomainValues[2] >= 0.0f && tempDomainValues[2] <= 1.0f);
292 SkASSERT(tempDomainValues[3] >= 0.0f && tempDomainValues[3] <= 1.0f);
Brian Salomon246bc3d2018-12-06 15:33:02 -0500293 }
Robert Phillipse98234f2017-01-09 14:23:59 -0500294
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000295 // vertical flip if necessary
Robert Phillipsbd99c0c2019-12-12 13:26:58 +0000296 if (kBottomLeft_GrSurfaceOrigin == origin) {
Brian Salomon7eabfe82019-12-02 14:20:20 -0500297 tempDomainValues[1] = h - tempDomainValues[1];
298 tempDomainValues[3] = h - tempDomainValues[3];
Brian Salomon246bc3d2018-12-06 15:33:02 -0500299
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000300 // The top and bottom were just flipped, so correct the ordering
301 // of elements so that values = (l, t, r, b).
Ben Wagnerf08d1d02018-06-18 15:11:00 -0400302 using std::swap;
Brian Salomon7eabfe82019-12-02 14:20:20 -0500303 swap(tempDomainValues[1], tempDomainValues[3]);
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000304 }
Brian Salomon7eabfe82019-12-02 14:20:20 -0500305 values = tempDomainValues;
306 } else {
307 values = textureDomain.domain().asScalars();
308 }
309 if (!std::equal(values, values + 4, fPrevDomain)) {
310 pdman.set4fv(fDomainUni, 1, values);
311 std::copy_n(values, 4, fPrevDomain);
312 }
313 if (sendDecalData &&
314 !std::equal(decalFilterWeights, decalFilterWeights + 3, fPrevDeclFilterWeights)) {
315 pdman.set3fv(fDecalUni, 1, decalFilterWeights);
316 std::copy_n(decalFilterWeights, 3, fPrevDeclFilterWeights);
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000317 }
318}
319
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000320///////////////////////////////////////////////////////////////////////////////
Brian Salomon587e08f2017-01-27 10:59:27 -0500321
Brian Salomon7eabfe82019-12-02 14:20:20 -0500322std::unique_ptr<GrFragmentProcessor> GrDomainEffect::Make(std::unique_ptr<GrFragmentProcessor> fp,
323 const SkRect& domain,
324 GrTextureDomain::Mode mode,
325 bool decalIsFiltered) {
326 return Make(std::move(fp), domain, mode, mode, decalIsFiltered);
Michael Ludwigbe315a22018-12-17 09:50:51 -0500327}
328
Brian Salomon7eabfe82019-12-02 14:20:20 -0500329std::unique_ptr<GrFragmentProcessor> GrDomainEffect::Make(std::unique_ptr<GrFragmentProcessor> fp,
330 const SkRect& domain,
331 GrTextureDomain::Mode modeX,
332 GrTextureDomain::Mode modeY,
333 bool decalIsFiltered) {
334 if (modeX == GrTextureDomain::kIgnore_Mode && modeY == GrTextureDomain::kIgnore_Mode) {
335 return fp;
336 }
337 int count = 0;
338 GrCoordTransform* coordTransform = nullptr;
339 for (auto [transform, ignored] : GrFragmentProcessor::FPCoordTransformRange(*fp)) {
340 ++count;
341 coordTransform = &transform;
342 }
343 // If there are no coord transforms on the passed FP or it's children then there's no need to
344 // enforce a domain.
345 // We have a limitation that only one coord transform is support when overriding local coords.
346 // If that limit were relaxed we would need to add a coord transform for each descendent FP
347 // transform and possibly have multiple domain rects to account for different proxy
348 // normalization and y-reversals.
349 if (count != 1) {
350 return fp;
351 }
352 GrCoordTransform transformCopy = *coordTransform;
353 // Reset the child FP's coord transform.
354 *coordTransform = {};
Michael Ludwigbe315a22018-12-17 09:50:51 -0500355 // 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 -0500356 // entirely and return the original FP. We'd need a GrMatrixProcessor if the matrix is not
357 // identity, though.
358 return std::unique_ptr<GrFragmentProcessor>(new GrDomainEffect(
359 std::move(fp), transformCopy, domain, modeX, modeY, decalIsFiltered));
Robert Phillips40fd7c92017-01-30 08:06:27 -0500360}
361
Brian Salomon7eabfe82019-12-02 14:20:20 -0500362std::unique_ptr<GrFragmentProcessor> GrDomainEffect::Make(std::unique_ptr<GrFragmentProcessor> fp,
363 const SkRect& domain,
364 GrTextureDomain::Mode mode,
365 GrSamplerState::Filter filter) {
366 bool filterIfDecal = filter != GrSamplerState::Filter::kNearest;
367 return Make(std::move(fp), domain, mode, filterIfDecal);
368}
369
370std::unique_ptr<GrFragmentProcessor> GrDomainEffect::Make(std::unique_ptr<GrFragmentProcessor> fp,
371 const SkRect& domain,
372 GrTextureDomain::Mode modeX,
373 GrTextureDomain::Mode modeY,
374 GrSamplerState::Filter filter) {
375 bool filterIfDecal = filter != GrSamplerState::Filter::kNearest;
376 return Make(std::move(fp), domain, modeX, modeY, filterIfDecal);
377}
378GrFragmentProcessor::OptimizationFlags GrDomainEffect::Flags(GrFragmentProcessor* fp,
379 GrTextureDomain::Mode modeX,
380 GrTextureDomain::Mode modeY) {
381 auto fpFlags = GrFragmentProcessor::ProcessorOptimizationFlags(fp);
382 if (modeX == GrTextureDomain::kDecal_Mode || modeY == GrTextureDomain::kDecal_Mode) {
383 return fpFlags & ~kPreservesOpaqueInput_OptimizationFlag;
384 }
385 return fpFlags;
386}
387
388GrDomainEffect::GrDomainEffect(std::unique_ptr<GrFragmentProcessor> fp,
389 const GrCoordTransform& coordTransform,
390 const SkRect& domain,
391 GrTextureDomain::Mode modeX,
392 GrTextureDomain::Mode modeY,
393 bool decalIsFiltered)
394 : INHERITED(kGrDomainEffect_ClassID, Flags(fp.get(), modeX, modeY))
395 , fCoordTransform(coordTransform)
396 , fDomain(domain, modeX, modeY)
397 , fDecalIsFiltered(decalIsFiltered) {
398 SkASSERT(fp);
399 fp->setSampledWithExplicitCoords(true);
400 this->registerChildProcessor(std::move(fp));
Brian Salomon6cd51b52017-07-26 19:07:15 -0400401 this->addCoordTransform(&fCoordTransform);
Brian Salomon7eabfe82019-12-02 14:20:20 -0500402 if (fDomain.modeX() != GrTextureDomain::kDecal_Mode &&
403 fDomain.modeY() != GrTextureDomain::kDecal_Mode) {
404 // Canonicalize this don't care value so we don't have to worry about it elsewhere.
405 fDecalIsFiltered = false;
406 }
Robert Phillips40fd7c92017-01-30 08:06:27 -0500407}
408
Brian Salomon7eabfe82019-12-02 14:20:20 -0500409GrDomainEffect::GrDomainEffect(const GrDomainEffect& that)
410 : INHERITED(kGrDomainEffect_ClassID, that.optimizationFlags())
Brian Salomon3f6f9652017-07-28 07:34:05 -0400411 , fCoordTransform(that.fCoordTransform)
Brian Salomon7eabfe82019-12-02 14:20:20 -0500412 , fDomain(that.fDomain)
413 , fDecalIsFiltered(that.fDecalIsFiltered) {
414 auto child = that.childProcessor(0).clone();
415 child->setSampledWithExplicitCoords(true);
416 this->registerChildProcessor(std::move(child));
Brian Salomon3f6f9652017-07-28 07:34:05 -0400417 this->addCoordTransform(&fCoordTransform);
Brian Salomon3f6f9652017-07-28 07:34:05 -0400418}
419
Brian Salomon7eabfe82019-12-02 14:20:20 -0500420void GrDomainEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
421 GrProcessorKeyBuilder* b) const {
422 b->add32(GrTextureDomain::GLDomain::DomainKey(fDomain));
joshualitteb2a6762014-12-04 11:35:33 -0800423}
424
Brian Salomon7eabfe82019-12-02 14:20:20 -0500425GrGLSLFragmentProcessor* GrDomainEffect::onCreateGLSLInstance() const {
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400426 class GLSLProcessor : public GrGLSLFragmentProcessor {
427 public:
428 void emitCode(EmitArgs& args) override {
Brian Salomon7eabfe82019-12-02 14:20:20 -0500429 const GrDomainEffect& de = args.fFp.cast<GrDomainEffect>();
430 const GrTextureDomain& domain = de.fDomain;
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400431
Ethan Nicholasd4efe682019-08-29 16:10:13 -0400432 SkString coords2D =
Brian Salomon7eabfe82019-12-02 14:20:20 -0500433 args.fFragBuilder->ensureCoords2D(args.fTransformedCoords[0].fVaryingPoint);
Brian Osmanc4689632016-12-19 17:04:59 -0500434
Brian Salomon7eabfe82019-12-02 14:20:20 -0500435 fGLDomain.sampleProcessor(domain, args.fInputColor, args.fOutputColor, coords2D, this,
436 args, 0);
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400437 }
438
439 protected:
Brian Salomonab015ef2017-04-04 10:15:51 -0400440 void onSetData(const GrGLSLProgramDataManager& pdman,
441 const GrFragmentProcessor& fp) override {
Brian Salomon7eabfe82019-12-02 14:20:20 -0500442 const GrDomainEffect& de = fp.cast<GrDomainEffect>();
443 const GrTextureDomain& domain = de.fDomain;
Robert Phillipsbd99c0c2019-12-12 13:26:58 +0000444 // TODO: Update GrCoordTransform to return a view instead of proxy
445 const GrSurfaceProxy* proxy = de.fCoordTransform.proxy();
446 // If we don't have a proxy the value of the origin doesn't matter
447 GrSurfaceOrigin origin = proxy ? proxy->origin() : kTopLeft_GrSurfaceOrigin;
448 fGLDomain.setData(pdman, domain, proxy, origin, de.fDecalIsFiltered);
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400449 }
450
451 private:
452 GrTextureDomain::GLDomain fGLDomain;
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400453 };
454
455 return new GLSLProcessor;
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000456}
457
Brian Salomon7eabfe82019-12-02 14:20:20 -0500458bool GrDomainEffect::onIsEqual(const GrFragmentProcessor& sBase) const {
459 auto& td = sBase.cast<GrDomainEffect>();
460 return fDomain == td.fDomain && fDecalIsFiltered == td.fDecalIsFiltered;
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000461}
462
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000463///////////////////////////////////////////////////////////////////////////////
464
Brian Salomon7eabfe82019-12-02 14:20:20 -0500465GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrDomainEffect);
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000466
Hal Canary6f6961e2017-01-31 13:50:44 -0500467#if GR_TEST_UTILS
Brian Salomon7eabfe82019-12-02 14:20:20 -0500468std::unique_ptr<GrFragmentProcessor> GrDomainEffect::TestCreate(GrProcessorTestData* d) {
469 do {
470 GrTextureDomain::Mode modeX =
471 (GrTextureDomain::Mode)d->fRandom->nextULessThan(GrTextureDomain::kModeCount);
472 GrTextureDomain::Mode modeY =
473 (GrTextureDomain::Mode)d->fRandom->nextULessThan(GrTextureDomain::kModeCount);
474 auto child = GrProcessorUnitTest::MakeChildFP(d);
475 const auto* childPtr = child.get();
476 SkRect domain;
477 // We assert if the child's coord transform has a proxy and the domain rect is outside its
478 // bounds.
479 GrFragmentProcessor::CoordTransformIter ctIter(*child);
480 if (!ctIter) {
481 continue;
482 }
483 auto [transform, fp] = *ctIter;
484 if (auto proxy = transform.proxy()) {
485 auto [w, h] = proxy->backingStoreDimensions();
486 domain.fLeft = d->fRandom->nextRangeScalar(0, w);
487 domain.fRight = d->fRandom->nextRangeScalar(0, w);
488 domain.fTop = d->fRandom->nextRangeScalar(0, h);
489 domain.fBottom = d->fRandom->nextRangeScalar(0, h);
490 } else {
491 domain.fLeft = d->fRandom->nextRangeScalar(-100.f, 100.f);
492 domain.fRight = d->fRandom->nextRangeScalar(-100.f, 100.f);
493 domain.fTop = d->fRandom->nextRangeScalar(-100.f, 100.f);
494 domain.fBottom = d->fRandom->nextRangeScalar(-100.f, 100.f);
495 }
496 domain.sort();
497 bool filterIfDecal = d->fRandom->nextBool();
498 auto result = GrDomainEffect::Make(std::move(child), domain, modeX, modeY, filterIfDecal);
499 if (result && result.get() != childPtr) {
500 return result;
501 }
502 } while (true);
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400503}
Hal Canary6f6961e2017-01-31 13:50:44 -0500504#endif
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400505
506///////////////////////////////////////////////////////////////////////////////
Brian Salomonaff329b2017-08-11 09:40:37 -0400507std::unique_ptr<GrFragmentProcessor> GrDeviceSpaceTextureDecalFragmentProcessor::Make(
Michael Ludwig8fa469d2019-11-25 16:08:44 -0500508 sk_sp<GrSurfaceProxy> proxy, const SkIRect& subset, const SkIPoint& deviceSpaceOffset) {
Brian Salomonaff329b2017-08-11 09:40:37 -0400509 return std::unique_ptr<GrFragmentProcessor>(new GrDeviceSpaceTextureDecalFragmentProcessor(
Robert Phillipsfbcef6e2017-06-15 12:07:18 -0400510 std::move(proxy), subset, deviceSpaceOffset));
Robert Phillips40fd7c92017-01-30 08:06:27 -0500511}
512
513GrDeviceSpaceTextureDecalFragmentProcessor::GrDeviceSpaceTextureDecalFragmentProcessor(
Michael Ludwig8fa469d2019-11-25 16:08:44 -0500514 sk_sp<GrSurfaceProxy> proxy, const SkIRect& subset, const SkIPoint& deviceSpaceOffset)
Ethan Nicholasabff9562017-10-09 10:54:08 -0400515 : INHERITED(kGrDeviceSpaceTextureDecalFragmentProcessor_ClassID,
516 kCompatibleWithCoverageAsAlpha_OptimizationFlag)
Brian Salomonccb61422020-01-09 10:46:36 -0500517 , fTextureSampler(proxy, GrSamplerState::Filter::kNearest)
Michael Ludwigbe315a22018-12-17 09:50:51 -0500518 , fTextureDomain(proxy.get(),
519 GrTextureDomain::MakeTexelDomain(subset, GrTextureDomain::kDecal_Mode),
520 GrTextureDomain::kDecal_Mode, GrTextureDomain::kDecal_Mode) {
Brian Salomonf7dcd762018-07-30 14:48:15 -0400521 this->setTextureSamplerCnt(1);
Robert Phillips40fd7c92017-01-30 08:06:27 -0500522 fDeviceSpaceOffset.fX = deviceSpaceOffset.fX - subset.fLeft;
523 fDeviceSpaceOffset.fY = deviceSpaceOffset.fY - subset.fTop;
Robert Phillips40fd7c92017-01-30 08:06:27 -0500524}
525
Brian Salomon1a2a7ab2017-07-26 13:11:51 -0400526GrDeviceSpaceTextureDecalFragmentProcessor::GrDeviceSpaceTextureDecalFragmentProcessor(
527 const GrDeviceSpaceTextureDecalFragmentProcessor& that)
Ethan Nicholasabff9562017-10-09 10:54:08 -0400528 : INHERITED(kGrDeviceSpaceTextureDecalFragmentProcessor_ClassID,
529 kCompatibleWithCoverageAsAlpha_OptimizationFlag)
Brian Salomon1a2a7ab2017-07-26 13:11:51 -0400530 , fTextureSampler(that.fTextureSampler)
531 , fTextureDomain(that.fTextureDomain)
532 , fDeviceSpaceOffset(that.fDeviceSpaceOffset) {
Brian Salomonf7dcd762018-07-30 14:48:15 -0400533 this->setTextureSamplerCnt(1);
Brian Salomon1a2a7ab2017-07-26 13:11:51 -0400534}
535
Brian Salomonaff329b2017-08-11 09:40:37 -0400536std::unique_ptr<GrFragmentProcessor> GrDeviceSpaceTextureDecalFragmentProcessor::clone() const {
537 return std::unique_ptr<GrFragmentProcessor>(
538 new GrDeviceSpaceTextureDecalFragmentProcessor(*this));
Brian Salomon1a2a7ab2017-07-26 13:11:51 -0400539}
540
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400541GrGLSLFragmentProcessor* GrDeviceSpaceTextureDecalFragmentProcessor::onCreateGLSLInstance() const {
542 class GLSLProcessor : public GrGLSLFragmentProcessor {
543 public:
544 void emitCode(EmitArgs& args) override {
545 const GrDeviceSpaceTextureDecalFragmentProcessor& dstdfp =
546 args.fFp.cast<GrDeviceSpaceTextureDecalFragmentProcessor>();
547 const char* scaleAndTranslateName;
548 fScaleAndTranslateUni = args.fUniformHandler->addUniform(kFragment_GrShaderFlag,
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400549 kHalf4_GrSLType,
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400550 "scaleAndTranslate",
551 &scaleAndTranslateName);
Ethan Nicholase1f55022019-02-05 17:17:40 -0500552 args.fFragBuilder->codeAppendf("half2 coords = half2(sk_FragCoord.xy * %s.xy + %s.zw);",
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400553 scaleAndTranslateName, scaleAndTranslateName);
554 fGLDomain.sampleTexture(args.fFragBuilder,
555 args.fUniformHandler,
Brian Salomon1edc5b92016-11-29 13:43:46 -0500556 args.fShaderCaps,
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400557 dstdfp.fTextureDomain,
558 args.fOutputColor,
559 SkString("coords"),
560 args.fTexSamplers[0],
561 args.fInputColor);
562 }
563
564 protected:
Brian Salomonab015ef2017-04-04 10:15:51 -0400565 void onSetData(const GrGLSLProgramDataManager& pdman,
566 const GrFragmentProcessor& fp) override {
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400567 const GrDeviceSpaceTextureDecalFragmentProcessor& dstdfp =
568 fp.cast<GrDeviceSpaceTextureDecalFragmentProcessor>();
Robert Phillipsbd99c0c2019-12-12 13:26:58 +0000569 const auto& view = dstdfp.textureSampler(0).view();
570 SkISize textureDims = view.proxy()->backingStoreDimensions();
Robert Phillips9bee2e52017-05-29 12:37:20 -0400571
Robert Phillipsbd99c0c2019-12-12 13:26:58 +0000572 fGLDomain.setData(pdman, dstdfp.fTextureDomain, view,
Michael Ludwigbe315a22018-12-17 09:50:51 -0500573 dstdfp.textureSampler(0).samplerState());
Michael Ludwig8fa469d2019-11-25 16:08:44 -0500574 float iw = 1.f / textureDims.width();
575 float ih = 1.f / textureDims.height();
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400576 float scaleAndTransData[4] = {
577 iw, ih,
578 -dstdfp.fDeviceSpaceOffset.fX * iw, -dstdfp.fDeviceSpaceOffset.fY * ih
579 };
Robert Phillipsbd99c0c2019-12-12 13:26:58 +0000580 if (view.origin() == kBottomLeft_GrSurfaceOrigin) {
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400581 scaleAndTransData[1] = -scaleAndTransData[1];
582 scaleAndTransData[3] = 1 - scaleAndTransData[3];
583 }
584 pdman.set4fv(fScaleAndTranslateUni, 1, scaleAndTransData);
585 }
586
587 private:
588 GrTextureDomain::GLDomain fGLDomain;
589 UniformHandle fScaleAndTranslateUni;
590 };
591
592 return new GLSLProcessor;
593}
594
595bool GrDeviceSpaceTextureDecalFragmentProcessor::onIsEqual(const GrFragmentProcessor& fp) const {
596 const GrDeviceSpaceTextureDecalFragmentProcessor& dstdfp =
597 fp.cast<GrDeviceSpaceTextureDecalFragmentProcessor>();
Robert Phillipsbd99c0c2019-12-12 13:26:58 +0000598 return dstdfp.fTextureSampler.view().proxy()->underlyingUniqueID() ==
599 fTextureSampler.view().proxy()->underlyingUniqueID() &&
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400600 dstdfp.fDeviceSpaceOffset == fDeviceSpaceOffset &&
601 dstdfp.fTextureDomain == fTextureDomain;
602}
603
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400604///////////////////////////////////////////////////////////////////////////////
605
606GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrDeviceSpaceTextureDecalFragmentProcessor);
607
Hal Canary6f6961e2017-01-31 13:50:44 -0500608#if GR_TEST_UTILS
Brian Salomonaff329b2017-08-11 09:40:37 -0400609std::unique_ptr<GrFragmentProcessor> GrDeviceSpaceTextureDecalFragmentProcessor::TestCreate(
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400610 GrProcessorTestData* d) {
Brian Salomon766098d2019-12-18 10:41:58 -0500611 auto [proxy, at, ct] = d->randomProxy();
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400612 SkIRect subset;
Robert Phillips40fd7c92017-01-30 08:06:27 -0500613 subset.fLeft = d->fRandom->nextULessThan(proxy->width() - 1);
614 subset.fRight = d->fRandom->nextRangeU(subset.fLeft, proxy->width());
615 subset.fTop = d->fRandom->nextULessThan(proxy->height() - 1);
616 subset.fBottom = d->fRandom->nextRangeU(subset.fTop, proxy->height());
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400617 SkIPoint pt;
618 pt.fX = d->fRandom->nextULessThan(2048);
619 pt.fY = d->fRandom->nextULessThan(2048);
Robert Phillipsfbcef6e2017-06-15 12:07:18 -0400620 return GrDeviceSpaceTextureDecalFragmentProcessor::Make(std::move(proxy), subset, pt);
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000621}
Hal Canary6f6961e2017-01-31 13:50:44 -0500622#endif