• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
emitCode(EmitArgs & args)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 
GenKey(const GrProcessor & processor,const GrShaderCaps &,GrProcessorKeyBuilder * b)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 ///////////////////////////////////////////////////////////////////////////////
GrConfigConversionEffect(GrTexture * texture,PMConversion pmConversion,const SkMatrix & matrix)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 
GrConfigConversionEffect(GrResourceProvider * resourceProvider,sk_sp<GrTextureProxy> proxy,PMConversion pmConversion,const SkMatrix & matrix)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 
onIsEqual(const GrFragmentProcessor & s) const115 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
TestCreate(GrProcessorTestData * d)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 
onGetGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const147 void GrConfigConversionEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
148                                                      GrProcessorKeyBuilder* b) const {
149     GrGLConfigConversionEffect::GenKey(*this, caps, b);
150 }
151 
onCreateGLSLInstance() const152 GrGLSLFragmentProcessor* GrConfigConversionEffect::onCreateGLSLInstance() const {
153     return new GrGLConfigConversionEffect();
154 }
155 
156 
157 
TestForPreservingPMConversions(GrContext * context,PMConversion * pmToUPMRule,PMConversion * upmToPMRule)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 
Make(GrTexture * texture,PMConversion pmConversion,const SkMatrix & matrix)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 
Make(GrResourceProvider * resourceProvider,sk_sp<GrTextureProxy> proxy,PMConversion pmConversion,const SkMatrix & matrix)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