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 #include "GrTextureDomain.h" 9 10 #include "GrResourceProvider.h" 11 #include "GrShaderCaps.h" 12 #include "GrSimpleTextureEffect.h" 13 #include "GrSurfaceProxyPriv.h" 14 #include "GrTexture.h" 15 #include "SkFloatingPoint.h" 16 #include "glsl/GrGLSLColorSpaceXformHelper.h" 17 #include "glsl/GrGLSLFragmentProcessor.h" 18 #include "glsl/GrGLSLFragmentShaderBuilder.h" 19 #include "glsl/GrGLSLProgramDataManager.h" 20 #include "glsl/GrGLSLShaderBuilder.h" 21 #include "glsl/GrGLSLUniformHandler.h" 22 23 static bool can_ignore_rect(GrTextureProxy* proxy, const SkRect& domain) { 24 if (GrResourceProvider::IsFunctionallyExact(proxy)) { 25 const SkIRect kFullRect = SkIRect::MakeWH(proxy->width(), proxy->height()); 26 27 return domain.contains(kFullRect); 28 } 29 30 return false; 31 } 32 33 GrTextureDomain::GrTextureDomain(GrTextureProxy* proxy, const SkRect& domain, Mode mode, int index) 34 : fMode(mode) 35 , fIndex(index) { 36 37 if (kIgnore_Mode == fMode) { 38 return; 39 } 40 41 if (kClamp_Mode == mode && can_ignore_rect(proxy, domain)) { 42 fMode = kIgnore_Mode; 43 return; 44 } 45 46 const SkRect kFullRect = SkRect::MakeIWH(proxy->width(), proxy->height()); 47 48 // We don't currently handle domains that are empty or don't intersect the texture. 49 // It is OK if the domain rect is a line or point, but it should not be inverted. We do not 50 // handle rects that do not intersect the [0..1]x[0..1] rect. 51 SkASSERT(domain.fLeft <= domain.fRight); 52 SkASSERT(domain.fTop <= domain.fBottom); 53 fDomain.fLeft = SkScalarPin(domain.fLeft, 0.0f, kFullRect.fRight); 54 fDomain.fRight = SkScalarPin(domain.fRight, fDomain.fLeft, kFullRect.fRight); 55 fDomain.fTop = SkScalarPin(domain.fTop, 0.0f, kFullRect.fBottom); 56 fDomain.fBottom = SkScalarPin(domain.fBottom, fDomain.fTop, kFullRect.fBottom); 57 SkASSERT(fDomain.fLeft <= fDomain.fRight); 58 SkASSERT(fDomain.fTop <= fDomain.fBottom); 59 } 60 61 ////////////////////////////////////////////////////////////////////////////// 62 63 void GrTextureDomain::GLDomain::sampleTexture(GrGLSLShaderBuilder* builder, 64 GrGLSLUniformHandler* uniformHandler, 65 const GrShaderCaps* shaderCaps, 66 const GrTextureDomain& textureDomain, 67 const char* outColor, 68 const SkString& inCoords, 69 GrGLSLFragmentProcessor::SamplerHandle sampler, 70 const char* inModulateColor, 71 GrGLSLColorSpaceXformHelper* colorXformHelper) { 72 SkASSERT((Mode)-1 == fMode || textureDomain.mode() == fMode); 73 SkDEBUGCODE(fMode = textureDomain.mode();) 74 75 if (textureDomain.mode() != kIgnore_Mode && !fDomainUni.isValid()) { 76 const char* name; 77 SkString uniName("TexDom"); 78 if (textureDomain.fIndex >= 0) { 79 uniName.appendS32(textureDomain.fIndex); 80 } 81 fDomainUni = uniformHandler->addUniform(kFragment_GrShaderFlag, 82 kVec4f_GrSLType, kDefault_GrSLPrecision, 83 uniName.c_str(), &name); 84 fDomainName = name; 85 } 86 87 switch (textureDomain.mode()) { 88 case kIgnore_Mode: { 89 builder->codeAppendf("%s = ", outColor); 90 builder->appendTextureLookupAndModulate(inModulateColor, sampler, inCoords.c_str(), 91 kVec2f_GrSLType, colorXformHelper); 92 builder->codeAppend(";"); 93 break; 94 } 95 case kClamp_Mode: { 96 SkString clampedCoords; 97 clampedCoords.appendf("clamp(%s, %s.xy, %s.zw)", 98 inCoords.c_str(), fDomainName.c_str(), fDomainName.c_str()); 99 100 builder->codeAppendf("%s = ", outColor); 101 builder->appendTextureLookupAndModulate(inModulateColor, sampler, clampedCoords.c_str(), 102 kVec2f_GrSLType, colorXformHelper); 103 builder->codeAppend(";"); 104 break; 105 } 106 case kDecal_Mode: { 107 // Add a block since we're going to declare variables. 108 GrGLSLShaderBuilder::ShaderBlock block(builder); 109 110 const char* domain = fDomainName.c_str(); 111 if (!shaderCaps->canUseAnyFunctionInShader()) { 112 // On the NexusS and GalaxyNexus, the other path (with the 'any' 113 // call) causes the compilation error "Calls to any function that 114 // may require a gradient calculation inside a conditional block 115 // may return undefined results". This appears to be an issue with 116 // the 'any' call since even the simple "result=black; if (any()) 117 // result=white;" code fails to compile. 118 builder->codeAppend("vec4 outside = vec4(0.0, 0.0, 0.0, 0.0);"); 119 builder->codeAppend("vec4 inside = "); 120 builder->appendTextureLookupAndModulate(inModulateColor, sampler, inCoords.c_str(), 121 kVec2f_GrSLType, colorXformHelper); 122 builder->codeAppend(";"); 123 124 builder->codeAppendf("highp float x = (%s).x;", inCoords.c_str()); 125 builder->codeAppendf("highp float y = (%s).y;", inCoords.c_str()); 126 127 builder->codeAppendf("x = abs(2.0*(x - %s.x)/(%s.z - %s.x) - 1.0);", 128 domain, domain, domain); 129 builder->codeAppendf("y = abs(2.0*(y - %s.y)/(%s.w - %s.y) - 1.0);", 130 domain, domain, domain); 131 builder->codeAppend("float blend = step(1.0, max(x, y));"); 132 builder->codeAppendf("%s = mix(inside, outside, blend);", outColor); 133 } else { 134 builder->codeAppend("bvec4 outside;\n"); 135 builder->codeAppendf("outside.xy = lessThan(%s, %s.xy);", inCoords.c_str(), 136 domain); 137 builder->codeAppendf("outside.zw = greaterThan(%s, %s.zw);", inCoords.c_str(), 138 domain); 139 builder->codeAppendf("%s = any(outside) ? vec4(0.0, 0.0, 0.0, 0.0) : ", 140 outColor); 141 builder->appendTextureLookupAndModulate(inModulateColor, sampler, inCoords.c_str(), 142 kVec2f_GrSLType, colorXformHelper); 143 builder->codeAppend(";"); 144 } 145 break; 146 } 147 case kRepeat_Mode: { 148 SkString clampedCoords; 149 clampedCoords.printf("mod(%s - %s.xy, %s.zw - %s.xy) + %s.xy", 150 inCoords.c_str(), fDomainName.c_str(), fDomainName.c_str(), 151 fDomainName.c_str(), fDomainName.c_str()); 152 153 builder->codeAppendf("%s = ", outColor); 154 builder->appendTextureLookupAndModulate(inModulateColor, sampler, clampedCoords.c_str(), 155 kVec2f_GrSLType, colorXformHelper); 156 builder->codeAppend(";"); 157 break; 158 } 159 } 160 } 161 162 void GrTextureDomain::GLDomain::setData(const GrGLSLProgramDataManager& pdman, 163 const GrTextureDomain& textureDomain, 164 GrTexture* tex) { 165 SkASSERT(textureDomain.mode() == fMode); 166 if (kIgnore_Mode != textureDomain.mode()) { 167 SkScalar wInv = SK_Scalar1 / tex->width(); 168 SkScalar hInv = SK_Scalar1 / tex->height(); 169 170 float values[kPrevDomainCount] = { 171 SkScalarToFloat(textureDomain.domain().fLeft * wInv), 172 SkScalarToFloat(textureDomain.domain().fTop * hInv), 173 SkScalarToFloat(textureDomain.domain().fRight * wInv), 174 SkScalarToFloat(textureDomain.domain().fBottom * hInv) 175 }; 176 177 SkASSERT(values[0] >= 0.0f && values[0] <= 1.0f); 178 SkASSERT(values[1] >= 0.0f && values[1] <= 1.0f); 179 SkASSERT(values[2] >= 0.0f && values[2] <= 1.0f); 180 SkASSERT(values[3] >= 0.0f && values[3] <= 1.0f); 181 182 // vertical flip if necessary 183 if (kBottomLeft_GrSurfaceOrigin == tex->origin()) { 184 values[1] = 1.0f - values[1]; 185 values[3] = 1.0f - values[3]; 186 // The top and bottom were just flipped, so correct the ordering 187 // of elements so that values = (l, t, r, b). 188 SkTSwap(values[1], values[3]); 189 } 190 if (0 != memcmp(values, fPrevDomain, kPrevDomainCount * sizeof(float))) { 191 pdman.set4fv(fDomainUni, 1, values); 192 memcpy(fPrevDomain, values, kPrevDomainCount * sizeof(float)); 193 } 194 } 195 } 196 197 /////////////////////////////////////////////////////////////////////////////// 198 inline GrFragmentProcessor::OptimizationFlags GrTextureDomainEffect::OptFlags( 199 GrPixelConfig config, GrTextureDomain::Mode mode) { 200 if (mode == GrTextureDomain::kDecal_Mode || !GrPixelConfigIsOpaque(config)) { 201 return GrFragmentProcessor::kCompatibleWithCoverageAsAlpha_OptimizationFlag; 202 } else { 203 return GrFragmentProcessor::kCompatibleWithCoverageAsAlpha_OptimizationFlag | 204 GrFragmentProcessor::kPreservesOpaqueInput_OptimizationFlag; 205 } 206 } 207 208 sk_sp<GrFragmentProcessor> GrTextureDomainEffect::Make(sk_sp<GrTextureProxy> proxy, 209 sk_sp<GrColorSpaceXform> colorSpaceXform, 210 const SkMatrix& matrix, 211 const SkRect& domain, 212 GrTextureDomain::Mode mode, 213 GrSamplerParams::FilterMode filterMode) { 214 if (GrTextureDomain::kIgnore_Mode == mode || 215 (GrTextureDomain::kClamp_Mode == mode && can_ignore_rect(proxy.get(), domain))) { 216 return GrSimpleTextureEffect::Make(std::move(proxy), 217 std::move(colorSpaceXform), matrix, filterMode); 218 } else { 219 return sk_sp<GrFragmentProcessor>( 220 new GrTextureDomainEffect(std::move(proxy), 221 std::move(colorSpaceXform), 222 matrix, domain, mode, filterMode)); 223 } 224 } 225 226 GrTextureDomainEffect::GrTextureDomainEffect(sk_sp<GrTextureProxy> proxy, 227 sk_sp<GrColorSpaceXform> colorSpaceXform, 228 const SkMatrix& matrix, 229 const SkRect& domain, 230 GrTextureDomain::Mode mode, 231 GrSamplerParams::FilterMode filterMode) 232 : GrSingleTextureEffect(OptFlags(proxy->config(), mode), proxy, 233 std::move(colorSpaceXform), matrix, filterMode) 234 , fTextureDomain(proxy.get(), domain, mode) { 235 SkASSERT(mode != GrTextureDomain::kRepeat_Mode || 236 filterMode == GrSamplerParams::kNone_FilterMode); 237 this->initClassID<GrTextureDomainEffect>(); 238 } 239 240 void GrTextureDomainEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps, 241 GrProcessorKeyBuilder* b) const { 242 b->add32(GrTextureDomain::GLDomain::DomainKey(fTextureDomain)); 243 b->add32(GrColorSpaceXform::XformKey(this->colorSpaceXform())); 244 } 245 246 GrGLSLFragmentProcessor* GrTextureDomainEffect::onCreateGLSLInstance() const { 247 class GLSLProcessor : public GrGLSLFragmentProcessor { 248 public: 249 void emitCode(EmitArgs& args) override { 250 const GrTextureDomainEffect& tde = args.fFp.cast<GrTextureDomainEffect>(); 251 const GrTextureDomain& domain = tde.fTextureDomain; 252 253 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; 254 SkString coords2D = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]); 255 256 fColorSpaceHelper.emitCode(args.fUniformHandler, tde.colorSpaceXform()); 257 fGLDomain.sampleTexture(fragBuilder, 258 args.fUniformHandler, 259 args.fShaderCaps, 260 domain, 261 args.fOutputColor, 262 coords2D, 263 args.fTexSamplers[0], 264 args.fInputColor, 265 &fColorSpaceHelper); 266 } 267 268 protected: 269 void onSetData(const GrGLSLProgramDataManager& pdman, 270 const GrFragmentProcessor& fp) override { 271 const GrTextureDomainEffect& tde = fp.cast<GrTextureDomainEffect>(); 272 const GrTextureDomain& domain = tde.fTextureDomain; 273 GrTexture* texture = tde.textureSampler(0).peekTexture(); 274 275 fGLDomain.setData(pdman, domain, texture); 276 if (SkToBool(tde.colorSpaceXform())) { 277 fColorSpaceHelper.setData(pdman, tde.colorSpaceXform()); 278 } 279 } 280 281 private: 282 GrTextureDomain::GLDomain fGLDomain; 283 GrGLSLColorSpaceXformHelper fColorSpaceHelper; 284 }; 285 286 return new GLSLProcessor; 287 } 288 289 bool GrTextureDomainEffect::onIsEqual(const GrFragmentProcessor& sBase) const { 290 const GrTextureDomainEffect& s = sBase.cast<GrTextureDomainEffect>(); 291 return this->fTextureDomain == s.fTextureDomain; 292 } 293 294 /////////////////////////////////////////////////////////////////////////////// 295 296 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrTextureDomainEffect); 297 298 #if GR_TEST_UTILS 299 sk_sp<GrFragmentProcessor> GrTextureDomainEffect::TestCreate(GrProcessorTestData* d) { 300 int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx 301 : GrProcessorUnitTest::kAlphaTextureIdx; 302 sk_sp<GrTextureProxy> proxy = d->textureProxy(texIdx); 303 SkRect domain; 304 domain.fLeft = d->fRandom->nextRangeScalar(0, proxy->width()); 305 domain.fRight = d->fRandom->nextRangeScalar(domain.fLeft, proxy->width()); 306 domain.fTop = d->fRandom->nextRangeScalar(0, proxy->height()); 307 domain.fBottom = d->fRandom->nextRangeScalar(domain.fTop, proxy->height()); 308 GrTextureDomain::Mode mode = 309 (GrTextureDomain::Mode) d->fRandom->nextULessThan(GrTextureDomain::kModeCount); 310 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom); 311 bool bilerp = mode != GrTextureDomain::kRepeat_Mode ? d->fRandom->nextBool() : false; 312 sk_sp<GrColorSpaceXform> colorSpaceXform = GrTest::TestColorXform(d->fRandom); 313 return GrTextureDomainEffect::Make(std::move(proxy), 314 std::move(colorSpaceXform), 315 matrix, 316 domain, 317 mode, 318 bilerp ? GrSamplerParams::kBilerp_FilterMode 319 : GrSamplerParams::kNone_FilterMode); 320 } 321 #endif 322 323 /////////////////////////////////////////////////////////////////////////////// 324 sk_sp<GrFragmentProcessor> GrDeviceSpaceTextureDecalFragmentProcessor::Make( 325 sk_sp<GrTextureProxy> proxy, 326 const SkIRect& subset, 327 const SkIPoint& deviceSpaceOffset) { 328 return sk_sp<GrFragmentProcessor>(new GrDeviceSpaceTextureDecalFragmentProcessor( 329 std::move(proxy), subset, deviceSpaceOffset)); 330 } 331 332 GrDeviceSpaceTextureDecalFragmentProcessor::GrDeviceSpaceTextureDecalFragmentProcessor( 333 sk_sp<GrTextureProxy> proxy, 334 const SkIRect& subset, 335 const SkIPoint& deviceSpaceOffset) 336 : INHERITED(kCompatibleWithCoverageAsAlpha_OptimizationFlag) 337 , fTextureSampler(proxy, GrSamplerParams::ClampNoFilter()) 338 , fTextureDomain(proxy.get(), GrTextureDomain::MakeTexelDomain(subset), 339 GrTextureDomain::kDecal_Mode) { 340 this->addTextureSampler(&fTextureSampler); 341 fDeviceSpaceOffset.fX = deviceSpaceOffset.fX - subset.fLeft; 342 fDeviceSpaceOffset.fY = deviceSpaceOffset.fY - subset.fTop; 343 this->initClassID<GrDeviceSpaceTextureDecalFragmentProcessor>(); 344 } 345 346 GrGLSLFragmentProcessor* GrDeviceSpaceTextureDecalFragmentProcessor::onCreateGLSLInstance() const { 347 class GLSLProcessor : public GrGLSLFragmentProcessor { 348 public: 349 void emitCode(EmitArgs& args) override { 350 const GrDeviceSpaceTextureDecalFragmentProcessor& dstdfp = 351 args.fFp.cast<GrDeviceSpaceTextureDecalFragmentProcessor>(); 352 const char* scaleAndTranslateName; 353 fScaleAndTranslateUni = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, 354 kVec4f_GrSLType, 355 kDefault_GrSLPrecision, 356 "scaleAndTranslate", 357 &scaleAndTranslateName); 358 args.fFragBuilder->codeAppendf("vec2 coords = sk_FragCoord.xy * %s.xy + %s.zw;", 359 scaleAndTranslateName, scaleAndTranslateName); 360 fGLDomain.sampleTexture(args.fFragBuilder, 361 args.fUniformHandler, 362 args.fShaderCaps, 363 dstdfp.fTextureDomain, 364 args.fOutputColor, 365 SkString("coords"), 366 args.fTexSamplers[0], 367 args.fInputColor); 368 } 369 370 protected: 371 void onSetData(const GrGLSLProgramDataManager& pdman, 372 const GrFragmentProcessor& fp) override { 373 const GrDeviceSpaceTextureDecalFragmentProcessor& dstdfp = 374 fp.cast<GrDeviceSpaceTextureDecalFragmentProcessor>(); 375 GrTexture* texture = dstdfp.textureSampler(0).peekTexture(); 376 377 fGLDomain.setData(pdman, dstdfp.fTextureDomain, texture); 378 float iw = 1.f / texture->width(); 379 float ih = 1.f / texture->height(); 380 float scaleAndTransData[4] = { 381 iw, ih, 382 -dstdfp.fDeviceSpaceOffset.fX * iw, -dstdfp.fDeviceSpaceOffset.fY * ih 383 }; 384 if (texture->origin() == kBottomLeft_GrSurfaceOrigin) { 385 scaleAndTransData[1] = -scaleAndTransData[1]; 386 scaleAndTransData[3] = 1 - scaleAndTransData[3]; 387 } 388 pdman.set4fv(fScaleAndTranslateUni, 1, scaleAndTransData); 389 } 390 391 private: 392 GrTextureDomain::GLDomain fGLDomain; 393 UniformHandle fScaleAndTranslateUni; 394 }; 395 396 return new GLSLProcessor; 397 } 398 399 bool GrDeviceSpaceTextureDecalFragmentProcessor::onIsEqual(const GrFragmentProcessor& fp) const { 400 const GrDeviceSpaceTextureDecalFragmentProcessor& dstdfp = 401 fp.cast<GrDeviceSpaceTextureDecalFragmentProcessor>(); 402 return dstdfp.fTextureSampler.proxy()->underlyingUniqueID() == 403 fTextureSampler.proxy()->underlyingUniqueID() && 404 dstdfp.fDeviceSpaceOffset == fDeviceSpaceOffset && 405 dstdfp.fTextureDomain == fTextureDomain; 406 } 407 408 /////////////////////////////////////////////////////////////////////////////// 409 410 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrDeviceSpaceTextureDecalFragmentProcessor); 411 412 #if GR_TEST_UTILS 413 sk_sp<GrFragmentProcessor> GrDeviceSpaceTextureDecalFragmentProcessor::TestCreate( 414 GrProcessorTestData* d) { 415 int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx 416 : GrProcessorUnitTest::kAlphaTextureIdx; 417 sk_sp<GrTextureProxy> proxy = d->textureProxy(texIdx); 418 SkIRect subset; 419 subset.fLeft = d->fRandom->nextULessThan(proxy->width() - 1); 420 subset.fRight = d->fRandom->nextRangeU(subset.fLeft, proxy->width()); 421 subset.fTop = d->fRandom->nextULessThan(proxy->height() - 1); 422 subset.fBottom = d->fRandom->nextRangeU(subset.fTop, proxy->height()); 423 SkIPoint pt; 424 pt.fX = d->fRandom->nextULessThan(2048); 425 pt.fY = d->fRandom->nextULessThan(2048); 426 return GrDeviceSpaceTextureDecalFragmentProcessor::Make(std::move(proxy), subset, pt); 427 } 428 #endif 429