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 "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