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