Line data Source code
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 "SkMatrix.h"
14 : #include "glsl/GrGLSLFragmentProcessor.h"
15 : #include "glsl/GrGLSLFragmentShaderBuilder.h"
16 :
17 0 : class GrGLConfigConversionEffect : public GrGLSLFragmentProcessor {
18 : public:
19 0 : void emitCode(EmitArgs& args) override {
20 0 : const GrConfigConversionEffect& cce = args.fFp.cast<GrConfigConversionEffect>();
21 0 : GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
22 :
23 : // Use highp throughout the shader to avoid some precision issues on specific GPUs.
24 0 : fragBuilder->elevateDefaultPrecision(kHigh_GrSLPrecision);
25 :
26 0 : if (nullptr == args.fInputColor) {
27 : // could optimize this case, but we aren't for now.
28 0 : args.fInputColor = "vec4(1)";
29 : }
30 :
31 : // Aggressively round to the nearest exact (N / 255) floating point value. This lets us
32 : // find a round-trip preserving pair on some GPUs that do odd byte to float conversion.
33 0 : fragBuilder->codeAppendf("vec4 color = floor(%s * 255.0 + 0.5) / 255.0;",
34 0 : args.fInputColor);
35 :
36 0 : switch (cce.pmConversion()) {
37 : case GrConfigConversionEffect::kMulByAlpha_RoundUp_PMConversion:
38 0 : fragBuilder->codeAppend(
39 0 : "color.rgb = ceil(color.rgb * color.a * 255.0) / 255.0;");
40 0 : break;
41 : case GrConfigConversionEffect::kMulByAlpha_RoundDown_PMConversion:
42 : // Add a compensation(0.001) here to avoid the side effect of the floor operation.
43 : // In Intel GPUs, the integer value converted from floor(%s.r * 255.0) / 255.0
44 : // is less than the integer value converted from %s.r by 1 when the %s.r is
45 : // converted from the integer value 2^n, such as 1, 2, 4, 8, etc.
46 0 : fragBuilder->codeAppend(
47 0 : "color.rgb = floor(color.rgb * color.a * 255.0 + 0.001) / 255.0;");
48 0 : break;
49 : case GrConfigConversionEffect::kMulByAlpha_RoundNearest_PMConversion:
50 0 : fragBuilder->codeAppend(
51 0 : "color.rgb = floor(color.rgb * color.a * 255.0 + 0.5) / 255.0;");
52 0 : break;
53 :
54 : case GrConfigConversionEffect::kDivByAlpha_RoundUp_PMConversion:
55 0 : fragBuilder->codeAppend(
56 0 : "color.rgb = color.a <= 0.0 ? vec3(0,0,0) : ceil(color.rgb / color.a * 255.0) / 255.0;");
57 0 : break;
58 : case GrConfigConversionEffect::kDivByAlpha_RoundDown_PMConversion:
59 0 : fragBuilder->codeAppend(
60 0 : "color.rgb = color.a <= 0.0 ? vec3(0,0,0) : floor(color.rgb / color.a * 255.0) / 255.0;");
61 0 : break;
62 : case GrConfigConversionEffect::kDivByAlpha_RoundNearest_PMConversion:
63 0 : fragBuilder->codeAppend(
64 0 : "color.rgb = color.a <= 0.0 ? vec3(0,0,0) : floor(color.rgb / color.a * 255.0 + 0.5) / 255.0;");
65 0 : break;
66 :
67 : default:
68 0 : SkFAIL("Unknown conversion op.");
69 0 : break;
70 : }
71 0 : fragBuilder->codeAppendf("%s = color;", args.fOutputColor);
72 0 : }
73 :
74 0 : static inline void GenKey(const GrProcessor& processor, const GrShaderCaps&,
75 : GrProcessorKeyBuilder* b) {
76 0 : const GrConfigConversionEffect& cce = processor.cast<GrConfigConversionEffect>();
77 0 : uint32_t key = cce.pmConversion();
78 0 : b->add32(key);
79 0 : }
80 :
81 : private:
82 : typedef GrGLSLFragmentProcessor INHERITED;
83 :
84 : };
85 :
86 : ///////////////////////////////////////////////////////////////////////////////
87 :
88 0 : GrConfigConversionEffect::GrConfigConversionEffect(PMConversion pmConversion)
89 : : INHERITED(kNone_OptimizationFlags)
90 0 : , fPMConversion(pmConversion) {
91 0 : this->initClassID<GrConfigConversionEffect>();
92 0 : }
93 :
94 0 : bool GrConfigConversionEffect::onIsEqual(const GrFragmentProcessor& s) const {
95 0 : const GrConfigConversionEffect& other = s.cast<GrConfigConversionEffect>();
96 0 : return other.fPMConversion == fPMConversion;
97 : }
98 :
99 : ///////////////////////////////////////////////////////////////////////////////
100 :
101 : GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrConfigConversionEffect);
102 :
103 : #if GR_TEST_UTILS
104 0 : sk_sp<GrFragmentProcessor> GrConfigConversionEffect::TestCreate(GrProcessorTestData* d) {
105 0 : PMConversion pmConv = static_cast<PMConversion>(d->fRandom->nextULessThan(kPMConversionCnt));
106 0 : return sk_sp<GrFragmentProcessor>(new GrConfigConversionEffect(pmConv));
107 : }
108 : #endif
109 :
110 : ///////////////////////////////////////////////////////////////////////////////
111 :
112 0 : void GrConfigConversionEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
113 : GrProcessorKeyBuilder* b) const {
114 0 : GrGLConfigConversionEffect::GenKey(*this, caps, b);
115 0 : }
116 :
117 0 : GrGLSLFragmentProcessor* GrConfigConversionEffect::onCreateGLSLInstance() const {
118 0 : return new GrGLConfigConversionEffect();
119 : }
120 :
121 :
122 :
123 0 : void GrConfigConversionEffect::TestForPreservingPMConversions(GrContext* context,
124 : PMConversion* pmToUPMRule,
125 : PMConversion* upmToPMRule) {
126 0 : *pmToUPMRule = kPMConversionCnt;
127 0 : *upmToPMRule = kPMConversionCnt;
128 : static constexpr int kSize = 256;
129 : static constexpr GrPixelConfig kConfig = kRGBA_8888_GrPixelConfig;
130 0 : SkAutoTMalloc<uint32_t> data(kSize * kSize * 3);
131 0 : uint32_t* srcData = data.get();
132 0 : uint32_t* firstRead = data.get() + kSize * kSize;
133 0 : uint32_t* secondRead = data.get() + 2 * kSize * kSize;
134 :
135 : // Fill with every possible premultiplied A, color channel value. There will be 256-y duplicate
136 : // values in row y. We set r,g, and b to the same value since they are handled identically.
137 0 : for (int y = 0; y < kSize; ++y) {
138 0 : for (int x = 0; x < kSize; ++x) {
139 0 : uint8_t* color = reinterpret_cast<uint8_t*>(&srcData[kSize*y + x]);
140 0 : color[3] = y;
141 0 : color[2] = SkTMin(x, y);
142 0 : color[1] = SkTMin(x, y);
143 0 : color[0] = SkTMin(x, y);
144 : }
145 : }
146 :
147 : const SkImageInfo ii = SkImageInfo::Make(kSize, kSize,
148 0 : kRGBA_8888_SkColorType, kPremul_SkAlphaType);
149 :
150 : sk_sp<GrRenderTargetContext> readRTC(context->makeRenderTargetContext(SkBackingFit::kExact,
151 : kSize, kSize,
152 0 : kConfig, nullptr));
153 : sk_sp<GrRenderTargetContext> tempRTC(context->makeRenderTargetContext(SkBackingFit::kExact,
154 : kSize, kSize,
155 0 : kConfig, nullptr));
156 0 : if (!readRTC || !tempRTC) {
157 0 : return;
158 : }
159 0 : GrSurfaceDesc desc;
160 0 : desc.fWidth = kSize;
161 0 : desc.fHeight = kSize;
162 0 : desc.fConfig = kConfig;
163 :
164 0 : GrResourceProvider* resourceProvider = context->resourceProvider();
165 : sk_sp<GrTextureProxy> dataProxy = GrSurfaceProxy::MakeDeferred(resourceProvider, desc,
166 0 : SkBudgeted::kYes, data, 0);
167 0 : if (!dataProxy) {
168 0 : return;
169 : }
170 :
171 : static const PMConversion kConversionRules[][2] = {
172 : {kDivByAlpha_RoundNearest_PMConversion, kMulByAlpha_RoundNearest_PMConversion},
173 : {kDivByAlpha_RoundDown_PMConversion, kMulByAlpha_RoundUp_PMConversion},
174 : {kDivByAlpha_RoundUp_PMConversion, kMulByAlpha_RoundDown_PMConversion},
175 : };
176 :
177 0 : uint32_t bestFailCount = 0xFFFFFFFF;
178 0 : size_t bestRule = 0;
179 :
180 0 : for (size_t i = 0; i < SK_ARRAY_COUNT(kConversionRules) && bestFailCount; ++i) {
181 0 : *pmToUPMRule = kConversionRules[i][0];
182 0 : *upmToPMRule = kConversionRules[i][1];
183 :
184 0 : static const SkRect kDstRect = SkRect::MakeIWH(kSize, kSize);
185 0 : static const SkRect kSrcRect = SkRect::MakeIWH(kSize, kSize);
186 : // We do a PM->UPM draw from dataTex to readTex and read the data. Then we do a UPM->PM draw
187 : // from readTex to tempTex followed by a PM->UPM draw to readTex and finally read the data.
188 : // We then verify that two reads produced the same values.
189 :
190 0 : if (!readRTC->asTextureProxy()) {
191 0 : continue;
192 : }
193 0 : GrPaint paint1;
194 0 : GrPaint paint2;
195 0 : GrPaint paint3;
196 0 : sk_sp<GrFragmentProcessor> pmToUPM(new GrConfigConversionEffect(*pmToUPMRule));
197 0 : sk_sp<GrFragmentProcessor> upmToPM(new GrConfigConversionEffect(*upmToPMRule));
198 :
199 0 : paint1.addColorTextureProcessor(resourceProvider, dataProxy, nullptr, SkMatrix::I());
200 0 : paint1.addColorFragmentProcessor(pmToUPM);
201 0 : paint1.setPorterDuffXPFactory(SkBlendMode::kSrc);
202 :
203 0 : readRTC->fillRectToRect(GrNoClip(), std::move(paint1), GrAA::kNo, SkMatrix::I(), kDstRect,
204 0 : kSrcRect);
205 :
206 0 : if (!readRTC->readPixels(ii, firstRead, 0, 0, 0)) {
207 0 : continue;
208 : }
209 :
210 0 : paint2.addColorTextureProcessor(resourceProvider, readRTC->asTextureProxyRef(), nullptr,
211 0 : SkMatrix::I());
212 0 : paint2.addColorFragmentProcessor(std::move(upmToPM));
213 0 : paint2.setPorterDuffXPFactory(SkBlendMode::kSrc);
214 :
215 0 : tempRTC->fillRectToRect(GrNoClip(), std::move(paint2), GrAA::kNo, SkMatrix::I(), kDstRect,
216 0 : kSrcRect);
217 :
218 0 : paint3.addColorTextureProcessor(resourceProvider, tempRTC->asTextureProxyRef(), nullptr,
219 0 : SkMatrix::I());
220 0 : paint3.addColorFragmentProcessor(std::move(pmToUPM));
221 0 : paint3.setPorterDuffXPFactory(SkBlendMode::kSrc);
222 :
223 0 : readRTC->fillRectToRect(GrNoClip(), std::move(paint3), GrAA::kNo, SkMatrix::I(), kDstRect,
224 0 : kSrcRect);
225 :
226 0 : if (!readRTC->readPixels(ii, secondRead, 0, 0, 0)) {
227 0 : continue;
228 : }
229 :
230 0 : uint32_t failCount = 0;
231 0 : for (int y = 0; y < kSize; ++y) {
232 0 : for (int x = 0; x <= y; ++x) {
233 0 : if (firstRead[kSize * y + x] != secondRead[kSize * y + x]) {
234 0 : if (++failCount >= bestFailCount) {
235 0 : break;
236 : }
237 : }
238 : }
239 : }
240 0 : if (failCount < bestFailCount) {
241 0 : bestFailCount = failCount;
242 0 : bestRule = i;
243 : }
244 : }
245 0 : if (bestFailCount > 0) {
246 0 : *pmToUPMRule = kPMConversionCnt;
247 0 : *upmToPMRule = kPMConversionCnt;
248 : } else {
249 0 : *pmToUPMRule = kConversionRules[bestRule][0];
250 0 : *upmToPMRule = kConversionRules[bestRule][1];
251 : }
252 : }
253 :
254 0 : sk_sp<GrFragmentProcessor> GrConfigConversionEffect::Make(sk_sp<GrFragmentProcessor> fp,
255 : PMConversion pmConversion) {
256 0 : if (!fp) {
257 0 : return nullptr;
258 : }
259 0 : sk_sp<GrFragmentProcessor> ccFP(new GrConfigConversionEffect(pmConversion));
260 0 : sk_sp<GrFragmentProcessor> fpPipeline[] = { fp, ccFP };
261 0 : return GrFragmentProcessor::RunInSeries(fpPipeline, 2);
262 : }
|