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 "GrConfigConversionEffect.h"
      9 #include "../private/GrGLSL.h"
     10 #include "GrClip.h"
     11 #include "GrContext.h"
     12 #include "GrRenderTargetContext.h"
     13 #include "GrSimpleTextureEffect.h"
     14 #include "SkMatrix.h"
     15 #include "glsl/GrGLSLFragmentProcessor.h"
     16 #include "glsl/GrGLSLFragmentShaderBuilder.h"
     17 
     18 class GrGLConfigConversionEffect : public GrGLSLFragmentProcessor {
     19 public:
     20     void emitCode(EmitArgs& args) override {
     21         const GrConfigConversionEffect& cce = args.fFp.cast<GrConfigConversionEffect>();
     22         GrConfigConversionEffect::PMConversion pmConversion = cce.pmConversion();
     23 
     24         // Using highp for GLES here in order to avoid some precision issues on specific GPUs.
     25         GrShaderVar tmpVar("tmpColor", kVec4f_GrSLType, 0, kHigh_GrSLPrecision);
     26         SkString tmpDecl;
     27         tmpVar.appendDecl(args.fShaderCaps, &tmpDecl);
     28 
     29         GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
     30 
     31         fragBuilder->codeAppendf("%s;", tmpDecl.c_str());
     32 
     33         fragBuilder->codeAppendf("%s = ", tmpVar.c_str());
     34         fragBuilder->appendTextureLookup(args.fTexSamplers[0], args.fTransformedCoords[0].c_str(),
     35                                          args.fTransformedCoords[0].getType());
     36         fragBuilder->codeAppend(";");
     37 
     38         switch (pmConversion) {
     39             case GrConfigConversionEffect::kMulByAlpha_RoundUp_PMConversion:
     40                 fragBuilder->codeAppendf(
     41                     "%s = vec4(ceil(%s.rgb * %s.a * 255.0) / 255.0, %s.a);",
     42                     tmpVar.c_str(), tmpVar.c_str(), tmpVar.c_str(), tmpVar.c_str());
     43                 break;
     44             case GrConfigConversionEffect::kMulByAlpha_RoundDown_PMConversion:
     45                 // Add a compensation(0.001) here to avoid the side effect of the floor operation.
     46                 // In Intel GPUs, the integer value converted from floor(%s.r * 255.0) / 255.0
     47                 // is less than the integer value converted from  %s.r by 1 when the %s.r is
     48                 // converted from the integer value 2^n, such as 1, 2, 4, 8, etc.
     49                 fragBuilder->codeAppendf(
     50                     "%s = vec4(floor(%s.rgb * %s.a * 255.0 + 0.001) / 255.0, %s.a);",
     51                     tmpVar.c_str(), tmpVar.c_str(), tmpVar.c_str(), tmpVar.c_str());
     52 
     53                 break;
     54             case GrConfigConversionEffect::kDivByAlpha_RoundUp_PMConversion:
     55                 fragBuilder->codeAppendf(
     56                     "%s = %s.a <= 0.0 ? vec4(0,0,0,0) : vec4(ceil(%s.rgb / %s.a * 255.0) / 255.0, %s.a);",
     57                     tmpVar.c_str(), tmpVar.c_str(), tmpVar.c_str(), tmpVar.c_str(),
     58                     tmpVar.c_str());
     59                 break;
     60             case GrConfigConversionEffect::kDivByAlpha_RoundDown_PMConversion:
     61                 fragBuilder->codeAppendf(
     62                     "%s = %s.a <= 0.0 ? vec4(0,0,0,0) : vec4(floor(%s.rgb / %s.a * 255.0) / 255.0, %s.a);",
     63                     tmpVar.c_str(), tmpVar.c_str(), tmpVar.c_str(), tmpVar.c_str(),
     64                     tmpVar.c_str());
     65                 break;
     66             default:
     67                 SkFAIL("Unknown conversion op.");
     68                 break;
     69         }
     70         fragBuilder->codeAppendf("%s = %s;", args.fOutputColor, tmpVar.c_str());
     71 
     72         SkString modulate;
     73         GrGLSLMulVarBy4f(&modulate, args.fOutputColor, args.fInputColor);
     74         fragBuilder->codeAppend(modulate.c_str());
     75     }
     76 
     77     static inline void GenKey(const GrProcessor& processor, const GrShaderCaps&,
     78                               GrProcessorKeyBuilder* b) {
     79         const GrConfigConversionEffect& cce = processor.cast<GrConfigConversionEffect>();
     80         uint32_t key = cce.pmConversion();
     81         b->add32(key);
     82     }
     83 
     84 private:
     85     typedef GrGLSLFragmentProcessor INHERITED;
     86 
     87 };
     88 
     89 ///////////////////////////////////////////////////////////////////////////////
     90 GrConfigConversionEffect::GrConfigConversionEffect(GrTexture* texture,
     91                                                    PMConversion pmConversion,
     92                                                    const SkMatrix& matrix)
     93         : INHERITED(texture, nullptr, matrix, kNone_OptimizationFlags)
     94         , fPMConversion(pmConversion) {
     95     this->initClassID<GrConfigConversionEffect>();
     96     // We expect to get here with non-BGRA/RGBA only if we're doing not doing a premul/unpremul
     97     // conversion.
     98     SkASSERT(kRGBA_8888_GrPixelConfig == texture->config() ||
     99              kBGRA_8888_GrPixelConfig == texture->config());
    100 }
    101 
    102 GrConfigConversionEffect::GrConfigConversionEffect(GrResourceProvider* resourceProvider,
    103                                                    sk_sp<GrTextureProxy> proxy,
    104                                                    PMConversion pmConversion,
    105                                                    const SkMatrix& matrix)
    106         : INHERITED(resourceProvider, kNone_OptimizationFlags, proxy, nullptr, matrix)
    107         , fPMConversion(pmConversion) {
    108     this->initClassID<GrConfigConversionEffect>();
    109     // We expect to get here with non-BGRA/RGBA only if we're doing not doing a premul/unpremul
    110     // conversion.
    111     SkASSERT(kRGBA_8888_GrPixelConfig == proxy->config() ||
    112              kBGRA_8888_GrPixelConfig == proxy->config());
    113 }
    114 
    115 bool GrConfigConversionEffect::onIsEqual(const GrFragmentProcessor& s) const {
    116     const GrConfigConversionEffect& other = s.cast<GrConfigConversionEffect>();
    117     return other.fPMConversion == fPMConversion;
    118 }
    119 
    120 ///////////////////////////////////////////////////////////////////////////////
    121 
    122 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrConfigConversionEffect);
    123 
    124 #if !defined(__clang__) && _MSC_FULL_VER >= 190024213
    125 // Work around VS 2015 Update 3 optimizer bug that causes internal compiler error
    126 //https://connect.microsoft.com/VisualStudio/feedback/details/3100520/internal-compiler-error
    127 #pragma optimize("t", off)
    128 #endif
    129 
    130 #if GR_TEST_UTILS
    131 sk_sp<GrFragmentProcessor> GrConfigConversionEffect::TestCreate(GrProcessorTestData* d) {
    132     PMConversion pmConv = static_cast<PMConversion>(d->fRandom->nextULessThan(kPMConversionCnt));
    133     return sk_sp<GrFragmentProcessor>(new GrConfigConversionEffect(
    134                             d->resourceProvider(),
    135                             d->textureProxy(GrProcessorUnitTest::kSkiaPMTextureIdx),
    136                             pmConv, GrTest::TestMatrix(d->fRandom)));
    137 }
    138 #endif
    139 
    140 #if !defined(__clang__) && _MSC_FULL_VER >= 190024213
    141 // Restore optimization settings.
    142 #pragma optimize("", on)
    143 #endif
    144 
    145 ///////////////////////////////////////////////////////////////////////////////
    146 
    147 void GrConfigConversionEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
    148                                                      GrProcessorKeyBuilder* b) const {
    149     GrGLConfigConversionEffect::GenKey(*this, caps, b);
    150 }
    151 
    152 GrGLSLFragmentProcessor* GrConfigConversionEffect::onCreateGLSLInstance() const {
    153     return new GrGLConfigConversionEffect();
    154 }
    155 
    156 
    157 
    158 void GrConfigConversionEffect::TestForPreservingPMConversions(GrContext* context,
    159                                                               PMConversion* pmToUPMRule,
    160                                                               PMConversion* upmToPMRule) {
    161     *pmToUPMRule = kPMConversionCnt;
    162     *upmToPMRule = kPMConversionCnt;
    163     static constexpr int kSize = 256;
    164     static constexpr GrPixelConfig kConfig = kRGBA_8888_GrPixelConfig;
    165     SkAutoTMalloc<uint32_t> data(kSize * kSize * 3);
    166     uint32_t* srcData = data.get();
    167     uint32_t* firstRead = data.get() + kSize * kSize;
    168     uint32_t* secondRead = data.get() + 2 * kSize * kSize;
    169 
    170     // Fill with every possible premultiplied A, color channel value. There will be 256-y duplicate
    171     // values in row y. We set r,g, and b to the same value since they are handled identically.
    172     for (int y = 0; y < kSize; ++y) {
    173         for (int x = 0; x < kSize; ++x) {
    174             uint8_t* color = reinterpret_cast<uint8_t*>(&srcData[kSize*y + x]);
    175             color[3] = y;
    176             color[2] = SkTMin(x, y);
    177             color[1] = SkTMin(x, y);
    178             color[0] = SkTMin(x, y);
    179         }
    180     }
    181 
    182     const SkImageInfo ii = SkImageInfo::Make(kSize, kSize,
    183                                              kRGBA_8888_SkColorType, kPremul_SkAlphaType);
    184 
    185     sk_sp<GrRenderTargetContext> readRTC(context->makeRenderTargetContext(SkBackingFit::kExact,
    186                                                                           kSize, kSize,
    187                                                                           kConfig, nullptr));
    188     sk_sp<GrRenderTargetContext> tempRTC(context->makeRenderTargetContext(SkBackingFit::kExact,
    189                                                                           kSize, kSize,
    190                                                                           kConfig, nullptr));
    191     if (!readRTC || !tempRTC) {
    192         return;
    193     }
    194     GrSurfaceDesc desc;
    195     desc.fWidth = kSize;
    196     desc.fHeight = kSize;
    197     desc.fConfig = kConfig;
    198 
    199     GrResourceProvider* resourceProvider = context->resourceProvider();
    200     sk_sp<GrTextureProxy> dataProxy = GrSurfaceProxy::MakeDeferred(resourceProvider, desc,
    201                                                                    SkBudgeted::kYes, data, 0);
    202     if (!dataProxy) {
    203         return;
    204     }
    205 
    206     static const PMConversion kConversionRules[][2] = {
    207         {kDivByAlpha_RoundDown_PMConversion, kMulByAlpha_RoundUp_PMConversion},
    208         {kDivByAlpha_RoundUp_PMConversion, kMulByAlpha_RoundDown_PMConversion},
    209     };
    210 
    211     bool failed = true;
    212 
    213     for (size_t i = 0; i < SK_ARRAY_COUNT(kConversionRules) && failed; ++i) {
    214         *pmToUPMRule = kConversionRules[i][0];
    215         *upmToPMRule = kConversionRules[i][1];
    216 
    217         static const SkRect kDstRect = SkRect::MakeIWH(kSize, kSize);
    218         static const SkRect kSrcRect = SkRect::MakeIWH(kSize, kSize);
    219         // We do a PM->UPM draw from dataTex to readTex and read the data. Then we do a UPM->PM draw
    220         // from readTex to tempTex followed by a PM->UPM draw to readTex and finally read the data.
    221         // We then verify that two reads produced the same values.
    222 
    223         if (!readRTC->asTexture()) {
    224             continue;
    225         }
    226         GrPaint paint1;
    227         GrPaint paint2;
    228         GrPaint paint3;
    229         sk_sp<GrFragmentProcessor> pmToUPM1(new GrConfigConversionEffect(
    230                 resourceProvider, dataProxy, *pmToUPMRule, SkMatrix::I()));
    231         sk_sp<GrFragmentProcessor> upmToPM(new GrConfigConversionEffect(
    232                 resourceProvider, readRTC->asTextureProxyRef(), *upmToPMRule, SkMatrix::I()));
    233         sk_sp<GrFragmentProcessor> pmToUPM2(new GrConfigConversionEffect(
    234                 resourceProvider, tempRTC->asTextureProxyRef(), *pmToUPMRule, SkMatrix::I()));
    235 
    236         paint1.addColorFragmentProcessor(std::move(pmToUPM1));
    237         paint1.setPorterDuffXPFactory(SkBlendMode::kSrc);
    238 
    239         readRTC->fillRectToRect(GrNoClip(), std::move(paint1), GrAA::kNo, SkMatrix::I(), kDstRect,
    240                                 kSrcRect);
    241 
    242         if (!readRTC->readPixels(ii, firstRead, 0, 0, 0)) {
    243             continue;
    244         }
    245 
    246         paint2.addColorFragmentProcessor(std::move(upmToPM));
    247         paint2.setPorterDuffXPFactory(SkBlendMode::kSrc);
    248 
    249         tempRTC->fillRectToRect(GrNoClip(), std::move(paint2), GrAA::kNo, SkMatrix::I(), kDstRect,
    250                                 kSrcRect);
    251 
    252         paint3.addColorFragmentProcessor(std::move(pmToUPM2));
    253         paint3.setPorterDuffXPFactory(SkBlendMode::kSrc);
    254 
    255         readRTC->fillRectToRect(GrNoClip(), std::move(paint3), GrAA::kNo, SkMatrix::I(), kDstRect,
    256                                 kSrcRect);
    257 
    258         if (!readRTC->readPixels(ii, secondRead, 0, 0, 0)) {
    259             continue;
    260         }
    261 
    262         failed = false;
    263         for (int y = 0; y < kSize && !failed; ++y) {
    264             for (int x = 0; x <= y; ++x) {
    265                 if (firstRead[kSize * y + x] != secondRead[kSize * y + x]) {
    266                     failed = true;
    267                     break;
    268                 }
    269             }
    270         }
    271     }
    272     if (failed) {
    273         *pmToUPMRule = kPMConversionCnt;
    274         *upmToPMRule = kPMConversionCnt;
    275     }
    276 }
    277 
    278 sk_sp<GrFragmentProcessor> GrConfigConversionEffect::Make(GrTexture* texture,
    279                                                           PMConversion pmConversion,
    280                                                           const SkMatrix& matrix) {
    281     if (kRGBA_8888_GrPixelConfig != texture->config() &&
    282         kBGRA_8888_GrPixelConfig != texture->config()) {
    283         // The PM conversions assume colors are 0..255
    284         return nullptr;
    285     }
    286     return sk_sp<GrFragmentProcessor>(
    287         new GrConfigConversionEffect(texture, pmConversion, matrix));
    288 }
    289 
    290 sk_sp<GrFragmentProcessor> GrConfigConversionEffect::Make(GrResourceProvider* resourceProvider,
    291                                                           sk_sp<GrTextureProxy> proxy,
    292                                                           PMConversion pmConversion,
    293                                                           const SkMatrix& matrix) {
    294     if (kRGBA_8888_GrPixelConfig != proxy->config() &&
    295         kBGRA_8888_GrPixelConfig != proxy->config()) {
    296         // The PM conversions assume colors are 0..255
    297         return nullptr;
    298     }
    299     return sk_sp<GrFragmentProcessor>(new GrConfigConversionEffect(resourceProvider,
    300                                                                    std::move(proxy),
    301                                                                    pmConversion, matrix));
    302 }
    303