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