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