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