1 | /* |
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 | |
8 | #ifndef GrTextureDomain_DEFINED |
9 | #define GrTextureDomain_DEFINED |
10 | |
11 | #include "src/gpu/GrCoordTransform.h" |
12 | #include "src/gpu/GrFragmentProcessor.h" |
13 | #include "src/gpu/glsl/GrGLSLFragmentProcessor.h" |
14 | #include "src/gpu/glsl/GrGLSLProgramDataManager.h" |
15 | |
16 | class GrGLProgramBuilder; |
17 | class GrGLSLShaderBuilder; |
18 | class GrInvariantOutput; |
19 | class GrGLSLUniformHandler; |
20 | struct SkRect; |
21 | |
22 | /** |
23 | * Limits a texture's lookup coordinates to a domain. Samples outside the domain are either clamped |
24 | * the edge of the domain or result in a half4 of zeros (decal mode). The domain is clipped to |
25 | * normalized texture coords ([0,1]x[0,1] square). Bilinear filtering can cause texels outside the |
26 | * domain to affect the read value unless the caller considers this when calculating the domain. |
27 | */ |
28 | class GrTextureDomain { |
29 | public: |
30 | enum Mode { |
31 | // Ignore the texture domain rectangle. |
32 | kIgnore_Mode, |
33 | // Clamp texture coords to the domain rectangle. |
34 | kClamp_Mode, |
35 | // Treat the area outside the domain rectangle as fully transparent. |
36 | kDecal_Mode, |
37 | // Wrap texture coordinates. NOTE: filtering may not work as expected because Bilerp will |
38 | // read texels outside of the domain. We could perform additional texture reads and filter |
39 | // in the shader, but are not currently doing this for performance reasons |
40 | kRepeat_Mode, |
41 | // Mirror wrap texture coordinates. NOTE: suffers the same filtering limitation as kRepeat. |
42 | kMirrorRepeat_Mode, |
43 | |
44 | kLastMode = kMirrorRepeat_Mode |
45 | }; |
46 | static const int kModeCount = kLastMode + 1; |
47 | |
48 | static const GrTextureDomain& IgnoredDomain() { |
49 | static const GrTextureDomain gDomain((GrTextureProxy*)nullptr, |
50 | SkRect::MakeEmpty(), kIgnore_Mode, kIgnore_Mode); |
51 | return gDomain; |
52 | } |
53 | |
54 | /** |
55 | * Construct a domain used to sample a GrFragmentProcessor. |
56 | * |
57 | * @param index Pass a value >= 0 if using multiple texture domains in the same effect. |
58 | * It is used to keep inserted variables from causing name collisions. |
59 | */ |
60 | GrTextureDomain(const SkRect& domain, Mode modeX, Mode modeY, int index = -1); |
61 | |
62 | /** |
63 | * Construct a domain used to directly sampler a texture. |
64 | * |
65 | * @param index Pass a value >= 0 if using multiple texture domains in the same effect. |
66 | * It is used to keep inserted variables from causing name collisions. |
67 | */ |
68 | GrTextureDomain(GrSurfaceProxy*, const SkRect& domain, Mode modeX, Mode modeY, int index = -1); |
69 | |
70 | GrTextureDomain(const GrTextureDomain&) = default; |
71 | |
72 | const SkRect& domain() const { return fDomain; } |
73 | Mode modeX() const { return fModeX; } |
74 | Mode modeY() const { return fModeY; } |
75 | |
76 | /* |
77 | * Computes a domain that bounds all the texels in texelRect, possibly insetting by half a pixel |
78 | * depending on the mode. The mode is used for both axes. |
79 | */ |
80 | static const SkRect MakeTexelDomain(const SkIRect& texelRect, Mode mode) { |
81 | return MakeTexelDomain(texelRect, mode, mode); |
82 | } |
83 | |
84 | static const SkRect MakeTexelDomain(const SkIRect& texelRect, Mode modeX, Mode modeY) { |
85 | // For Clamp and decal modes, inset by half a texel |
86 | SkScalar insetX = ((modeX == kClamp_Mode || modeX == kDecal_Mode) && texelRect.width() > 0) |
87 | ? SK_ScalarHalf : 0; |
88 | SkScalar insetY = ((modeY == kClamp_Mode || modeY == kDecal_Mode) && texelRect.height() > 0) |
89 | ? SK_ScalarHalf : 0; |
90 | return SkRect::MakeLTRB(texelRect.fLeft + insetX, texelRect.fTop + insetY, |
91 | texelRect.fRight - insetX, texelRect.fBottom - insetY); |
92 | } |
93 | |
94 | // Convenience to determine if any axis of a texture uses an explicit decal mode or the hardware |
95 | // clamp to border decal mode. |
96 | static bool IsDecalSampled(GrSamplerState::WrapMode wrapX, GrSamplerState::WrapMode wrapY, |
97 | Mode modeX, Mode modeY) { |
98 | return wrapX == GrSamplerState::WrapMode::kClampToBorder || |
99 | wrapY == GrSamplerState::WrapMode::kClampToBorder || |
100 | modeX == kDecal_Mode || |
101 | modeY == kDecal_Mode; |
102 | } |
103 | |
104 | static bool IsDecalSampled(const GrSamplerState::WrapMode wraps[2], Mode modeX, Mode modeY) { |
105 | return IsDecalSampled(wraps[0], wraps[1], modeX, modeY); |
106 | } |
107 | |
108 | static bool IsDecalSampled(GrSamplerState sampler, Mode modeX, Mode modeY) { |
109 | return IsDecalSampled(sampler.wrapModeX(), sampler.wrapModeY(), modeX, modeY); |
110 | } |
111 | |
112 | bool operator==(const GrTextureDomain& that) const { |
113 | return fModeX == that.fModeX && fModeY == that.fModeY && |
114 | (kIgnore_Mode == fModeX || (fDomain.fLeft == that.fDomain.fLeft && |
115 | fDomain.fRight == that.fDomain.fRight)) && |
116 | (kIgnore_Mode == fModeY || (fDomain.fTop == that.fDomain.fTop && |
117 | fDomain.fBottom == that.fDomain.fBottom)); |
118 | } |
119 | bool operator!=(const GrTextureDomain& that) const { return !(*this == that); } |
120 | |
121 | /** |
122 | * A GrGLSLFragmentProcessor subclass that corresponds to a GrProcessor subclass that uses |
123 | * GrTextureDomain should include this helper. It generates the texture domain GLSL, produces |
124 | * the part of the effect key that reflects the texture domain code, and performs the uniform |
125 | * uploads necessary for texture domains. |
126 | */ |
127 | class GLDomain { |
128 | public: |
129 | GLDomain() = default; |
130 | |
131 | /** |
132 | * Call this from GrGLSLFragmentProcessor::emitCode() to sample a child processor WRT the |
133 | * domain and mode. |
134 | * |
135 | * @param outcolor name of half4 variable to hold the sampled color. |
136 | * @param inCoords name of float2 variable containing the coords to be used with the |
137 | * domain. |
138 | * @param inColor color passed to the child processor. |
139 | */ |
140 | void sampleProcessor(const GrFragmentProcessor* owner, |
141 | const GrTextureDomain& textureDomain, |
142 | const char* inColor, |
143 | const char* outColor, |
144 | const SkString& inCoords, |
145 | GrGLSLFragmentProcessor* parent, |
146 | GrGLSLFragmentProcessor::EmitArgs& args, |
147 | int childIndex); |
148 | |
149 | /** |
150 | * Call this from GrGLSLFragmentProcessor::emitCode() to sample the texture WRT the domain |
151 | * and mode. |
152 | * |
153 | * @param outcolor name of half4 variable to hold the sampled color. |
154 | * @param inCoords name of float2 variable containing the coords to be used with the |
155 | * domain. |
156 | * @param inModulateColor if non-nullptr the sampled color will be modulated with this |
157 | * expression before being written to outColor. |
158 | */ |
159 | void sampleTexture(const GrFragmentProcessor* owner, |
160 | GrGLSLShaderBuilder* builder, |
161 | GrGLSLUniformHandler* uniformHandler, |
162 | const GrShaderCaps* shaderCaps, |
163 | const GrTextureDomain& textureDomain, |
164 | const char* outColor, |
165 | const SkString& inCoords, |
166 | GrGLSLFragmentProcessor::SamplerHandle sampler, |
167 | const char* inModulateColor = nullptr); |
168 | |
169 | /** |
170 | * Call this from GrGLSLFragmentProcessor::setData() to upload uniforms necessary for the |
171 | * domain. 'filterIfDecal' determines whether the transition to transparent black at the |
172 | * edge of domain is linearly interpolated over a unit interval or is "hard" when |
173 | * kDecal_Mode is used. |
174 | */ |
175 | void setData(const GrGLSLProgramDataManager&, const GrTextureDomain&, bool filterIfDecal); |
176 | |
177 | /** |
178 | * Call this from GrGLSLFragmentProcessor::setData() to upload uniforms necessary for the |
179 | * texture domain used with a texture proxy. The rectangle is automatically adjusted to |
180 | * account for the texture's origin. Filtering at the edge of the domain is inferred from |
181 | * the GrSamplerState's filter mode. |
182 | */ |
183 | void setData(const GrGLSLProgramDataManager&, const GrTextureDomain&, |
184 | const GrSurfaceProxyView&, GrSamplerState state); |
185 | /** Same as above but with direct control over decal filtering. */ |
186 | void setData(const GrGLSLProgramDataManager&, const GrTextureDomain&, const GrSurfaceProxy*, |
187 | GrSurfaceOrigin, bool filterIfDecal); |
188 | |
189 | enum { |
190 | kModeBits = 3, // See DomainKey(). |
191 | kDomainKeyBits = 4 |
192 | }; |
193 | |
194 | /** |
195 | * GrGLSLFragmentProcessor::GenKey() must call this and include the returned value in it's |
196 | * computed key. The returned will be limited to the lower kDomainKeyBits bits. |
197 | */ |
198 | static uint32_t DomainKey(const GrTextureDomain& domain) { |
199 | static_assert(kModeCount <= (1 << kModeBits)); |
200 | return domain.modeX() | (domain.modeY() << kModeBits); |
201 | } |
202 | |
203 | private: |
204 | // Takes a builder and a coord and appends to the builder a string that is an expression |
205 | // the evaluates to a half4 color. |
206 | using AppendSample = SkString(const char* coord); |
207 | |
208 | void sample(const GrFragmentProcessor* owner, |
209 | GrGLSLShaderBuilder* builder, |
210 | GrGLSLUniformHandler* uniformHandler, |
211 | const GrTextureDomain& textureDomain, |
212 | const char* outColor, |
213 | const SkString& inCoords, |
214 | const std::function<AppendSample>& color); |
215 | |
216 | SkDEBUGCODE(Mode fModeX;) |
217 | SkDEBUGCODE(Mode fModeY;) |
218 | SkDEBUGCODE(bool fHasMode = false;) |
219 | GrGLSLProgramDataManager::UniformHandle fDomainUni; |
220 | SkString fDomainName; |
221 | |
222 | // Only initialized if the domain has at least one decal axis |
223 | GrGLSLProgramDataManager::UniformHandle fDecalUni; |
224 | SkString fDecalName; |
225 | |
226 | float fPrevDomain[4] = {SK_FloatNaN}; |
227 | float fPrevDeclFilterWeights[3] = {SK_FloatNaN}; |
228 | }; |
229 | |
230 | protected: |
231 | SkRect fDomain; |
232 | Mode fModeX; |
233 | Mode fModeY; |
234 | int fIndex; |
235 | }; |
236 | |
237 | #endif |
238 | |