gfx/skia/trunk/src/gpu/effects/GrConfigConversionEffect.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/skia/trunk/src/gpu/effects/GrConfigConversionEffect.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,277 @@
     1.4 +/*
     1.5 + * Copyright 2012 Google Inc.
     1.6 + *
     1.7 + * Use of this source code is governed by a BSD-style license that can be
     1.8 + * found in the LICENSE file.
     1.9 + */
    1.10 +
    1.11 +#include "GrConfigConversionEffect.h"
    1.12 +#include "GrContext.h"
    1.13 +#include "GrTBackendEffectFactory.h"
    1.14 +#include "GrSimpleTextureEffect.h"
    1.15 +#include "gl/GrGLEffect.h"
    1.16 +#include "SkMatrix.h"
    1.17 +
    1.18 +class GrGLConfigConversionEffect : public GrGLEffect {
    1.19 +public:
    1.20 +    GrGLConfigConversionEffect(const GrBackendEffectFactory& factory,
    1.21 +                               const GrDrawEffect& drawEffect)
    1.22 +    : INHERITED (factory) {
    1.23 +        const GrConfigConversionEffect& effect = drawEffect.castEffect<GrConfigConversionEffect>();
    1.24 +        fSwapRedAndBlue = effect.swapsRedAndBlue();
    1.25 +        fPMConversion = effect.pmConversion();
    1.26 +    }
    1.27 +
    1.28 +    virtual void emitCode(GrGLShaderBuilder* builder,
    1.29 +                          const GrDrawEffect&,
    1.30 +                          EffectKey key,
    1.31 +                          const char* outputColor,
    1.32 +                          const char* inputColor,
    1.33 +                          const TransformedCoordsArray& coords,
    1.34 +                          const TextureSamplerArray& samplers) SK_OVERRIDE {
    1.35 +        builder->fsCodeAppendf("\t\t%s = ", outputColor);
    1.36 +        builder->fsAppendTextureLookup(samplers[0], coords[0].c_str(), coords[0].type());
    1.37 +        builder->fsCodeAppend(";\n");
    1.38 +        if (GrConfigConversionEffect::kNone_PMConversion == fPMConversion) {
    1.39 +            SkASSERT(fSwapRedAndBlue);
    1.40 +            builder->fsCodeAppendf("\t%s = %s.bgra;\n", outputColor, outputColor);
    1.41 +        } else {
    1.42 +            const char* swiz = fSwapRedAndBlue ? "bgr" : "rgb";
    1.43 +            switch (fPMConversion) {
    1.44 +                case GrConfigConversionEffect::kMulByAlpha_RoundUp_PMConversion:
    1.45 +                    builder->fsCodeAppendf(
    1.46 +                        "\t\t%s = vec4(ceil(%s.%s * %s.a * 255.0) / 255.0, %s.a);\n",
    1.47 +                        outputColor, outputColor, swiz, outputColor, outputColor);
    1.48 +                    break;
    1.49 +                case GrConfigConversionEffect::kMulByAlpha_RoundDown_PMConversion:
    1.50 +                    // Add a compensation(0.001) here to avoid the side effect of the floor operation.
    1.51 +                    // In Intel GPUs, the integer value converted from floor(%s.r * 255.0) / 255.0
    1.52 +                    // is less than the integer value converted from  %s.r by 1 when the %s.r is
    1.53 +                    // converted from the integer value 2^n, such as 1, 2, 4, 8, etc.
    1.54 +                    builder->fsCodeAppendf(
    1.55 +                        "\t\t%s = vec4(floor(%s.%s * %s.a * 255.0 + 0.001) / 255.0, %s.a);\n",
    1.56 +                        outputColor, outputColor, swiz, outputColor, outputColor);
    1.57 +                    break;
    1.58 +                case GrConfigConversionEffect::kDivByAlpha_RoundUp_PMConversion:
    1.59 +                    builder->fsCodeAppendf("\t\t%s = %s.a <= 0.0 ? vec4(0,0,0,0) : vec4(ceil(%s.%s / %s.a * 255.0) / 255.0, %s.a);\n",
    1.60 +                        outputColor, outputColor, outputColor, swiz, outputColor, outputColor);
    1.61 +                    break;
    1.62 +                case GrConfigConversionEffect::kDivByAlpha_RoundDown_PMConversion:
    1.63 +                    builder->fsCodeAppendf("\t\t%s = %s.a <= 0.0 ? vec4(0,0,0,0) : vec4(floor(%s.%s / %s.a * 255.0) / 255.0, %s.a);\n",
    1.64 +                        outputColor, outputColor, outputColor, swiz, outputColor, outputColor);
    1.65 +                    break;
    1.66 +                default:
    1.67 +                    GrCrash("Unknown conversion op.");
    1.68 +                    break;
    1.69 +            }
    1.70 +        }
    1.71 +        SkString modulate;
    1.72 +        GrGLSLMulVarBy4f(&modulate, 2, outputColor, inputColor);
    1.73 +        builder->fsCodeAppend(modulate.c_str());
    1.74 +    }
    1.75 +
    1.76 +    static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
    1.77 +        const GrConfigConversionEffect& conv = drawEffect.castEffect<GrConfigConversionEffect>();
    1.78 +        return static_cast<EffectKey>(conv.swapsRedAndBlue()) | (conv.pmConversion() << 1);
    1.79 +    }
    1.80 +
    1.81 +private:
    1.82 +    bool                                    fSwapRedAndBlue;
    1.83 +    GrConfigConversionEffect::PMConversion  fPMConversion;
    1.84 +
    1.85 +    typedef GrGLEffect INHERITED;
    1.86 +
    1.87 +};
    1.88 +
    1.89 +///////////////////////////////////////////////////////////////////////////////
    1.90 +
    1.91 +GrConfigConversionEffect::GrConfigConversionEffect(GrTexture* texture,
    1.92 +                                                   bool swapRedAndBlue,
    1.93 +                                                   PMConversion pmConversion,
    1.94 +                                                   const SkMatrix& matrix)
    1.95 +    : GrSingleTextureEffect(texture, matrix)
    1.96 +    , fSwapRedAndBlue(swapRedAndBlue)
    1.97 +    , fPMConversion(pmConversion) {
    1.98 +    SkASSERT(kRGBA_8888_GrPixelConfig == texture->config() ||
    1.99 +             kBGRA_8888_GrPixelConfig == texture->config());
   1.100 +    // Why did we pollute our texture cache instead of using a GrSingleTextureEffect?
   1.101 +    SkASSERT(swapRedAndBlue || kNone_PMConversion != pmConversion);
   1.102 +}
   1.103 +
   1.104 +const GrBackendEffectFactory& GrConfigConversionEffect::getFactory() const {
   1.105 +    return GrTBackendEffectFactory<GrConfigConversionEffect>::getInstance();
   1.106 +}
   1.107 +
   1.108 +bool GrConfigConversionEffect::onIsEqual(const GrEffect& s) const {
   1.109 +    const GrConfigConversionEffect& other = CastEffect<GrConfigConversionEffect>(s);
   1.110 +    return this->texture(0) == s.texture(0) &&
   1.111 +           other.fSwapRedAndBlue == fSwapRedAndBlue &&
   1.112 +           other.fPMConversion == fPMConversion;
   1.113 +}
   1.114 +
   1.115 +void GrConfigConversionEffect::getConstantColorComponents(GrColor* color,
   1.116 +                                                          uint32_t* validFlags) const {
   1.117 +    this->updateConstantColorComponentsForModulation(color, validFlags);
   1.118 +}
   1.119 +
   1.120 +///////////////////////////////////////////////////////////////////////////////
   1.121 +
   1.122 +GR_DEFINE_EFFECT_TEST(GrConfigConversionEffect);
   1.123 +
   1.124 +GrEffectRef* GrConfigConversionEffect::TestCreate(SkRandom* random,
   1.125 +                                                  GrContext*,
   1.126 +                                                  const GrDrawTargetCaps&,
   1.127 +                                                  GrTexture* textures[]) {
   1.128 +    PMConversion pmConv = static_cast<PMConversion>(random->nextULessThan(kPMConversionCnt));
   1.129 +    bool swapRB;
   1.130 +    if (kNone_PMConversion == pmConv) {
   1.131 +        swapRB = true;
   1.132 +    } else {
   1.133 +        swapRB = random->nextBool();
   1.134 +    }
   1.135 +    AutoEffectUnref effect(SkNEW_ARGS(GrConfigConversionEffect,
   1.136 +                                      (textures[GrEffectUnitTest::kSkiaPMTextureIdx],
   1.137 +                                       swapRB,
   1.138 +                                       pmConv,
   1.139 +                                       GrEffectUnitTest::TestMatrix(random))));
   1.140 +    return CreateEffectRef(effect);
   1.141 +}
   1.142 +
   1.143 +///////////////////////////////////////////////////////////////////////////////
   1.144 +void GrConfigConversionEffect::TestForPreservingPMConversions(GrContext* context,
   1.145 +                                                              PMConversion* pmToUPMRule,
   1.146 +                                                              PMConversion* upmToPMRule) {
   1.147 +    *pmToUPMRule = kNone_PMConversion;
   1.148 +    *upmToPMRule = kNone_PMConversion;
   1.149 +    SkAutoTMalloc<uint32_t> data(256 * 256 * 3);
   1.150 +    uint32_t* srcData = data.get();
   1.151 +    uint32_t* firstRead = data.get() + 256 * 256;
   1.152 +    uint32_t* secondRead = data.get() + 2 * 256 * 256;
   1.153 +
   1.154 +    // Fill with every possible premultiplied A, color channel value. There will be 256-y duplicate
   1.155 +    // values in row y. We set r,g, and b to the same value since they are handled identically.
   1.156 +    for (int y = 0; y < 256; ++y) {
   1.157 +        for (int x = 0; x < 256; ++x) {
   1.158 +            uint8_t* color = reinterpret_cast<uint8_t*>(&srcData[256*y + x]);
   1.159 +            color[3] = y;
   1.160 +            color[2] = GrMin(x, y);
   1.161 +            color[1] = GrMin(x, y);
   1.162 +            color[0] = GrMin(x, y);
   1.163 +        }
   1.164 +    }
   1.165 +
   1.166 +    GrTextureDesc desc;
   1.167 +    desc.fFlags = kRenderTarget_GrTextureFlagBit |
   1.168 +                  kNoStencil_GrTextureFlagBit;
   1.169 +    desc.fWidth = 256;
   1.170 +    desc.fHeight = 256;
   1.171 +    desc.fConfig = kRGBA_8888_GrPixelConfig;
   1.172 +
   1.173 +    SkAutoTUnref<GrTexture> readTex(context->createUncachedTexture(desc, NULL, 0));
   1.174 +    if (!readTex.get()) {
   1.175 +        return;
   1.176 +    }
   1.177 +    SkAutoTUnref<GrTexture> tempTex(context->createUncachedTexture(desc, NULL, 0));
   1.178 +    if (!tempTex.get()) {
   1.179 +        return;
   1.180 +    }
   1.181 +    desc.fFlags = kNone_GrTextureFlags;
   1.182 +    SkAutoTUnref<GrTexture> dataTex(context->createUncachedTexture(desc, data, 0));
   1.183 +    if (!dataTex.get()) {
   1.184 +        return;
   1.185 +    }
   1.186 +
   1.187 +    static const PMConversion kConversionRules[][2] = {
   1.188 +        {kDivByAlpha_RoundDown_PMConversion, kMulByAlpha_RoundUp_PMConversion},
   1.189 +        {kDivByAlpha_RoundUp_PMConversion, kMulByAlpha_RoundDown_PMConversion},
   1.190 +    };
   1.191 +
   1.192 +    GrContext::AutoWideOpenIdentityDraw awoid(context, NULL);
   1.193 +
   1.194 +    bool failed = true;
   1.195 +
   1.196 +    for (size_t i = 0; i < GR_ARRAY_COUNT(kConversionRules) && failed; ++i) {
   1.197 +        *pmToUPMRule = kConversionRules[i][0];
   1.198 +        *upmToPMRule = kConversionRules[i][1];
   1.199 +
   1.200 +        static const SkRect kDstRect = SkRect::MakeWH(SkIntToScalar(256), SkIntToScalar(256));
   1.201 +        static const SkRect kSrcRect = SkRect::MakeWH(SK_Scalar1, SK_Scalar1);
   1.202 +        // We do a PM->UPM draw from dataTex to readTex and read the data. Then we do a UPM->PM draw
   1.203 +        // from readTex to tempTex followed by a PM->UPM draw to readTex and finally read the data.
   1.204 +        // We then verify that two reads produced the same values.
   1.205 +
   1.206 +        AutoEffectUnref pmToUPM1(SkNEW_ARGS(GrConfigConversionEffect, (dataTex,
   1.207 +                                                                       false,
   1.208 +                                                                       *pmToUPMRule,
   1.209 +                                                                       SkMatrix::I())));
   1.210 +        AutoEffectUnref upmToPM(SkNEW_ARGS(GrConfigConversionEffect, (readTex,
   1.211 +                                                                      false,
   1.212 +                                                                      *upmToPMRule,
   1.213 +                                                                      SkMatrix::I())));
   1.214 +        AutoEffectUnref pmToUPM2(SkNEW_ARGS(GrConfigConversionEffect, (tempTex,
   1.215 +                                                                       false,
   1.216 +                                                                       *pmToUPMRule,
   1.217 +                                                                       SkMatrix::I())));
   1.218 +
   1.219 +        SkAutoTUnref<GrEffectRef> pmToUPMEffect1(CreateEffectRef(pmToUPM1));
   1.220 +        SkAutoTUnref<GrEffectRef> upmToPMEffect(CreateEffectRef(upmToPM));
   1.221 +        SkAutoTUnref<GrEffectRef> pmToUPMEffect2(CreateEffectRef(pmToUPM2));
   1.222 +
   1.223 +        context->setRenderTarget(readTex->asRenderTarget());
   1.224 +        GrPaint paint1;
   1.225 +        paint1.addColorEffect(pmToUPMEffect1);
   1.226 +        context->drawRectToRect(paint1, kDstRect, kSrcRect);
   1.227 +
   1.228 +        readTex->readPixels(0, 0, 256, 256, kRGBA_8888_GrPixelConfig, firstRead);
   1.229 +
   1.230 +        context->setRenderTarget(tempTex->asRenderTarget());
   1.231 +        GrPaint paint2;
   1.232 +        paint2.addColorEffect(upmToPMEffect);
   1.233 +        context->drawRectToRect(paint2, kDstRect, kSrcRect);
   1.234 +        context->setRenderTarget(readTex->asRenderTarget());
   1.235 +
   1.236 +        GrPaint paint3;
   1.237 +        paint3.addColorEffect(pmToUPMEffect2);
   1.238 +        context->drawRectToRect(paint3, kDstRect, kSrcRect);
   1.239 +
   1.240 +        readTex->readPixels(0, 0, 256, 256, kRGBA_8888_GrPixelConfig, secondRead);
   1.241 +
   1.242 +        failed = false;
   1.243 +        for (int y = 0; y < 256 && !failed; ++y) {
   1.244 +            for (int x = 0; x <= y; ++x) {
   1.245 +                if (firstRead[256 * y + x] != secondRead[256 * y + x]) {
   1.246 +                    failed = true;
   1.247 +                    break;
   1.248 +                }
   1.249 +            }
   1.250 +        }
   1.251 +    }
   1.252 +    if (failed) {
   1.253 +        *pmToUPMRule = kNone_PMConversion;
   1.254 +        *upmToPMRule = kNone_PMConversion;
   1.255 +    }
   1.256 +}
   1.257 +
   1.258 +const GrEffectRef* GrConfigConversionEffect::Create(GrTexture* texture,
   1.259 +                                                    bool swapRedAndBlue,
   1.260 +                                                    PMConversion pmConversion,
   1.261 +                                                    const SkMatrix& matrix) {
   1.262 +    if (!swapRedAndBlue && kNone_PMConversion == pmConversion) {
   1.263 +        // If we returned a GrConfigConversionEffect that was equivalent to a GrSimpleTextureEffect
   1.264 +        // then we may pollute our texture cache with redundant shaders. So in the case that no
   1.265 +        // conversions were requested we instead return a GrSimpleTextureEffect.
   1.266 +        return GrSimpleTextureEffect::Create(texture, matrix);
   1.267 +    } else {
   1.268 +        if (kRGBA_8888_GrPixelConfig != texture->config() &&
   1.269 +            kBGRA_8888_GrPixelConfig != texture->config() &&
   1.270 +            kNone_PMConversion != pmConversion) {
   1.271 +            // The PM conversions assume colors are 0..255
   1.272 +            return NULL;
   1.273 +        }
   1.274 +        AutoEffectUnref effect(SkNEW_ARGS(GrConfigConversionEffect, (texture,
   1.275 +                                                                     swapRedAndBlue,
   1.276 +                                                                     pmConversion,
   1.277 +                                                                     matrix)));
   1.278 +        return CreateEffectRef(effect);
   1.279 +    }
   1.280 +}

mercurial