Home | History | Annotate | Download | only in effects
      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