GrConfigConversionEffect.cpp revision 586d5d640b19860dfbbd903a5188da1bbbe87336
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 "GrContext.h" 10#include "GrInvariantOutput.h" 11#include "GrSimpleTextureEffect.h" 12#include "SkMatrix.h" 13#include "gl/GrGLProcessor.h" 14#include "gl/builders/GrGLProgramBuilder.h" 15 16class GrGLConfigConversionEffect : public GrGLFragmentProcessor { 17public: 18 GrGLConfigConversionEffect(const GrProcessor& processor) { 19 const GrConfigConversionEffect& configConversionEffect = 20 processor.cast<GrConfigConversionEffect>(); 21 fSwapRedAndBlue = configConversionEffect.swapsRedAndBlue(); 22 fPMConversion = configConversionEffect.pmConversion(); 23 } 24 25 virtual void emitCode(GrGLFPBuilder* builder, 26 const GrFragmentProcessor&, 27 const char* outputColor, 28 const char* inputColor, 29 const TransformedCoordsArray& coords, 30 const TextureSamplerArray& samplers) SK_OVERRIDE { 31 // Using highp for GLES here in order to avoid some precision issues on specific GPUs. 32 GrGLShaderVar tmpVar("tmpColor", kVec4f_GrSLType, 0, kHigh_GrSLPrecision); 33 SkString tmpDecl; 34 tmpVar.appendDecl(builder->ctxInfo(), &tmpDecl); 35 36 GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder(); 37 38 fsBuilder->codeAppendf("%s;", tmpDecl.c_str()); 39 40 fsBuilder->codeAppendf("%s = ", tmpVar.c_str()); 41 fsBuilder->appendTextureLookup(samplers[0], coords[0].c_str(), coords[0].getType()); 42 fsBuilder->codeAppend(";"); 43 44 if (GrConfigConversionEffect::kNone_PMConversion == fPMConversion) { 45 SkASSERT(fSwapRedAndBlue); 46 fsBuilder->codeAppendf("%s = %s.bgra;", outputColor, tmpVar.c_str()); 47 } else { 48 const char* swiz = fSwapRedAndBlue ? "bgr" : "rgb"; 49 switch (fPMConversion) { 50 case GrConfigConversionEffect::kMulByAlpha_RoundUp_PMConversion: 51 fsBuilder->codeAppendf( 52 "%s = vec4(ceil(%s.%s * %s.a * 255.0) / 255.0, %s.a);", 53 tmpVar.c_str(), tmpVar.c_str(), swiz, tmpVar.c_str(), tmpVar.c_str()); 54 break; 55 case GrConfigConversionEffect::kMulByAlpha_RoundDown_PMConversion: 56 // Add a compensation(0.001) here to avoid the side effect of the floor operation. 57 // In Intel GPUs, the integer value converted from floor(%s.r * 255.0) / 255.0 58 // is less than the integer value converted from %s.r by 1 when the %s.r is 59 // converted from the integer value 2^n, such as 1, 2, 4, 8, etc. 60 fsBuilder->codeAppendf( 61 "%s = vec4(floor(%s.%s * %s.a * 255.0 + 0.001) / 255.0, %s.a);", 62 tmpVar.c_str(), tmpVar.c_str(), swiz, tmpVar.c_str(), tmpVar.c_str()); 63 break; 64 case GrConfigConversionEffect::kDivByAlpha_RoundUp_PMConversion: 65 fsBuilder->codeAppendf( 66 "%s = %s.a <= 0.0 ? vec4(0,0,0,0) : vec4(ceil(%s.%s / %s.a * 255.0) / 255.0, %s.a);", 67 tmpVar.c_str(), tmpVar.c_str(), tmpVar.c_str(), swiz, tmpVar.c_str(), tmpVar.c_str()); 68 break; 69 case GrConfigConversionEffect::kDivByAlpha_RoundDown_PMConversion: 70 fsBuilder->codeAppendf( 71 "%s = %s.a <= 0.0 ? vec4(0,0,0,0) : vec4(floor(%s.%s / %s.a * 255.0) / 255.0, %s.a);", 72 tmpVar.c_str(), tmpVar.c_str(), tmpVar.c_str(), swiz, tmpVar.c_str(), tmpVar.c_str()); 73 break; 74 default: 75 SkFAIL("Unknown conversion op."); 76 break; 77 } 78 fsBuilder->codeAppendf("%s = %s;", outputColor, tmpVar.c_str()); 79 } 80 SkString modulate; 81 GrGLSLMulVarBy4f(&modulate, outputColor, inputColor); 82 fsBuilder->codeAppend(modulate.c_str()); 83 } 84 85 static inline void GenKey(const GrProcessor& processor, const GrGLCaps&, 86 GrProcessorKeyBuilder* b) { 87 const GrConfigConversionEffect& conv = processor.cast<GrConfigConversionEffect>(); 88 uint32_t key = (conv.swapsRedAndBlue() ? 0 : 1) | (conv.pmConversion() << 1); 89 b->add32(key); 90 } 91 92private: 93 bool fSwapRedAndBlue; 94 GrConfigConversionEffect::PMConversion fPMConversion; 95 96 typedef GrGLFragmentProcessor INHERITED; 97 98}; 99 100/////////////////////////////////////////////////////////////////////////////// 101 102GrConfigConversionEffect::GrConfigConversionEffect(GrTexture* texture, 103 bool swapRedAndBlue, 104 PMConversion pmConversion, 105 const SkMatrix& matrix) 106 : GrSingleTextureEffect(texture, matrix) 107 , fSwapRedAndBlue(swapRedAndBlue) 108 , fPMConversion(pmConversion) { 109 this->initClassID<GrConfigConversionEffect>(); 110 SkASSERT(kRGBA_8888_GrPixelConfig == texture->config() || 111 kBGRA_8888_GrPixelConfig == texture->config()); 112 // Why did we pollute our texture cache instead of using a GrSingleTextureEffect? 113 SkASSERT(swapRedAndBlue || kNone_PMConversion != pmConversion); 114} 115 116bool GrConfigConversionEffect::onIsEqual(const GrFragmentProcessor& s) const { 117 const GrConfigConversionEffect& other = s.cast<GrConfigConversionEffect>(); 118 return other.fSwapRedAndBlue == fSwapRedAndBlue && 119 other.fPMConversion == fPMConversion; 120} 121 122void GrConfigConversionEffect::onComputeInvariantOutput(GrInvariantOutput* inout) const { 123 this->updateInvariantOutputForModulation(inout); 124} 125 126/////////////////////////////////////////////////////////////////////////////// 127 128GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrConfigConversionEffect); 129 130GrFragmentProcessor* GrConfigConversionEffect::TestCreate(SkRandom* random, 131 GrContext*, 132 const GrDrawTargetCaps&, 133 GrTexture* textures[]) { 134 PMConversion pmConv = static_cast<PMConversion>(random->nextULessThan(kPMConversionCnt)); 135 bool swapRB; 136 if (kNone_PMConversion == pmConv) { 137 swapRB = true; 138 } else { 139 swapRB = random->nextBool(); 140 } 141 return SkNEW_ARGS(GrConfigConversionEffect, 142 (textures[GrProcessorUnitTest::kSkiaPMTextureIdx], 143 swapRB, 144 pmConv, 145 GrProcessorUnitTest::TestMatrix(random))); 146} 147 148/////////////////////////////////////////////////////////////////////////////// 149 150void GrConfigConversionEffect::getGLProcessorKey(const GrGLCaps& caps, 151 GrProcessorKeyBuilder* b) const { 152 GrGLConfigConversionEffect::GenKey(*this, caps, b); 153} 154 155GrGLFragmentProcessor* GrConfigConversionEffect::createGLInstance() const { 156 return SkNEW_ARGS(GrGLConfigConversionEffect, (*this)); 157} 158 159 160 161void GrConfigConversionEffect::TestForPreservingPMConversions(GrContext* context, 162 PMConversion* pmToUPMRule, 163 PMConversion* upmToPMRule) { 164 *pmToUPMRule = kNone_PMConversion; 165 *upmToPMRule = kNone_PMConversion; 166 SkAutoTMalloc<uint32_t> data(256 * 256 * 3); 167 uint32_t* srcData = data.get(); 168 uint32_t* firstRead = data.get() + 256 * 256; 169 uint32_t* secondRead = data.get() + 2 * 256 * 256; 170 171 // Fill with every possible premultiplied A, color channel value. There will be 256-y duplicate 172 // values in row y. We set r,g, and b to the same value since they are handled identically. 173 for (int y = 0; y < 256; ++y) { 174 for (int x = 0; x < 256; ++x) { 175 uint8_t* color = reinterpret_cast<uint8_t*>(&srcData[256*y + x]); 176 color[3] = y; 177 color[2] = SkTMin(x, y); 178 color[1] = SkTMin(x, y); 179 color[0] = SkTMin(x, y); 180 } 181 } 182 183 GrSurfaceDesc desc; 184 desc.fFlags = kRenderTarget_GrSurfaceFlag; 185 desc.fWidth = 256; 186 desc.fHeight = 256; 187 desc.fConfig = kRGBA_8888_GrPixelConfig; 188 189 SkAutoTUnref<GrTexture> readTex(context->createTexture(desc, true, NULL, 0)); 190 if (!readTex.get()) { 191 return; 192 } 193 SkAutoTUnref<GrTexture> tempTex(context->createTexture(desc, true, NULL, 0)); 194 if (!tempTex.get()) { 195 return; 196 } 197 desc.fFlags = kNone_GrSurfaceFlags; 198 SkAutoTUnref<GrTexture> dataTex(context->createTexture(desc, true, data, 0)); 199 if (!dataTex.get()) { 200 return; 201 } 202 203 static const PMConversion kConversionRules[][2] = { 204 {kDivByAlpha_RoundDown_PMConversion, kMulByAlpha_RoundUp_PMConversion}, 205 {kDivByAlpha_RoundUp_PMConversion, kMulByAlpha_RoundDown_PMConversion}, 206 }; 207 208 bool failed = true; 209 210 for (size_t i = 0; i < SK_ARRAY_COUNT(kConversionRules) && failed; ++i) { 211 *pmToUPMRule = kConversionRules[i][0]; 212 *upmToPMRule = kConversionRules[i][1]; 213 214 static const SkRect kDstRect = SkRect::MakeWH(SkIntToScalar(256), SkIntToScalar(256)); 215 static const SkRect kSrcRect = SkRect::MakeWH(SK_Scalar1, SK_Scalar1); 216 // We do a PM->UPM draw from dataTex to readTex and read the data. Then we do a UPM->PM draw 217 // from readTex to tempTex followed by a PM->UPM draw to readTex and finally read the data. 218 // We then verify that two reads produced the same values. 219 220 SkAutoTUnref<GrFragmentProcessor> pmToUPM1( 221 SkNEW_ARGS(GrConfigConversionEffect, 222 (dataTex, false, *pmToUPMRule, SkMatrix::I()))); 223 SkAutoTUnref<GrFragmentProcessor> upmToPM( 224 SkNEW_ARGS(GrConfigConversionEffect, 225 (readTex, false, *upmToPMRule, SkMatrix::I()))); 226 SkAutoTUnref<GrFragmentProcessor> pmToUPM2( 227 SkNEW_ARGS(GrConfigConversionEffect, 228 (tempTex, false, *pmToUPMRule, SkMatrix::I()))); 229 230 GrPaint paint1; 231 paint1.addColorProcessor(pmToUPM1); 232 context->drawNonAARectToRect(readTex->asRenderTarget(), 233 GrClip::WideOpen(), 234 paint1, 235 SkMatrix::I(), 236 kDstRect, 237 kSrcRect); 238 239 readTex->readPixels(0, 0, 256, 256, kRGBA_8888_GrPixelConfig, firstRead); 240 241 GrPaint paint2; 242 paint2.addColorProcessor(upmToPM); 243 context->drawNonAARectToRect(tempTex->asRenderTarget(), 244 GrClip::WideOpen(), 245 paint2, 246 SkMatrix::I(), 247 kDstRect, 248 kSrcRect); 249 250 GrPaint paint3; 251 paint3.addColorProcessor(pmToUPM2); 252 context->drawNonAARectToRect(readTex->asRenderTarget(), 253 GrClip::WideOpen(), 254 paint3, 255 SkMatrix::I(), 256 kDstRect, 257 kSrcRect); 258 259 readTex->readPixels(0, 0, 256, 256, kRGBA_8888_GrPixelConfig, secondRead); 260 261 failed = false; 262 for (int y = 0; y < 256 && !failed; ++y) { 263 for (int x = 0; x <= y; ++x) { 264 if (firstRead[256 * y + x] != secondRead[256 * y + x]) { 265 failed = true; 266 break; 267 } 268 } 269 } 270 } 271 if (failed) { 272 *pmToUPMRule = kNone_PMConversion; 273 *upmToPMRule = kNone_PMConversion; 274 } 275} 276 277const GrFragmentProcessor* GrConfigConversionEffect::Create(GrTexture* texture, 278 bool swapRedAndBlue, 279 PMConversion pmConversion, 280 const SkMatrix& matrix) { 281 if (!swapRedAndBlue && kNone_PMConversion == pmConversion) { 282 // If we returned a GrConfigConversionEffect that was equivalent to a GrSimpleTextureEffect 283 // then we may pollute our texture cache with redundant shaders. So in the case that no 284 // conversions were requested we instead return a GrSimpleTextureEffect. 285 return GrSimpleTextureEffect::Create(texture, matrix); 286 } else { 287 if (kRGBA_8888_GrPixelConfig != texture->config() && 288 kBGRA_8888_GrPixelConfig != texture->config() && 289 kNone_PMConversion != pmConversion) { 290 // The PM conversions assume colors are 0..255 291 return NULL; 292 } 293 return SkNEW_ARGS(GrConfigConversionEffect, (texture, 294 swapRedAndBlue, 295 pmConversion, 296 matrix)); 297 } 298} 299