1 /*
2 * Copyright 2015 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 "SkArithmeticMode_gpu.h"
9
10 #if SK_SUPPORT_GPU
11 #include "GrContext.h"
12 #include "GrFragmentProcessor.h"
13 #include "GrInvariantOutput.h"
14 #include "GrProcessor.h"
15 #include "GrTexture.h"
16 #include "gl/GrGLCaps.h"
17 #include "gl/GrGLProcessor.h"
18 #include "gl/GrGLProgramDataManager.h"
19 #include "gl/builders/GrGLProgramBuilder.h"
20
21 static const bool gUseUnpremul = false;
22
add_arithmetic_code(GrGLFragmentBuilder * fsBuilder,const char * inputColor,const char * dstColor,const char * outputColor,const char * kUni,bool enforcePMColor)23 static void add_arithmetic_code(GrGLFragmentBuilder* fsBuilder,
24 const char* inputColor,
25 const char* dstColor,
26 const char* outputColor,
27 const char* kUni,
28 bool enforcePMColor) {
29 // We don't try to optimize for this case at all
30 if (NULL == inputColor) {
31 fsBuilder->codeAppend("const vec4 src = vec4(1);");
32 } else {
33 fsBuilder->codeAppendf("vec4 src = %s;", inputColor);
34 if (gUseUnpremul) {
35 fsBuilder->codeAppend("src.rgb = clamp(src.rgb / src.a, 0.0, 1.0);");
36 }
37 }
38
39 fsBuilder->codeAppendf("vec4 dst = %s;", dstColor);
40 if (gUseUnpremul) {
41 fsBuilder->codeAppend("dst.rgb = clamp(dst.rgb / dst.a, 0.0, 1.0);");
42 }
43
44 fsBuilder->codeAppendf("%s = %s.x * src * dst + %s.y * src + %s.z * dst + %s.w;",
45 outputColor, kUni, kUni, kUni, kUni);
46 fsBuilder->codeAppendf("%s = clamp(%s, 0.0, 1.0);\n", outputColor, outputColor);
47 if (gUseUnpremul) {
48 fsBuilder->codeAppendf("%s.rgb *= %s.a;", outputColor, outputColor);
49 } else if (enforcePMColor) {
50 fsBuilder->codeAppendf("%s.rgb = min(%s.rgb, %s.a);",
51 outputColor, outputColor, outputColor);
52 }
53 }
54
55 class GLArithmeticFP : public GrGLFragmentProcessor {
56 public:
GLArithmeticFP(const GrProcessor &)57 GLArithmeticFP(const GrProcessor&)
58 : fEnforcePMColor(true) {
59 }
60
~GLArithmeticFP()61 ~GLArithmeticFP() override {}
62
emitCode(GrGLFPBuilder * builder,const GrFragmentProcessor & fp,const char * outputColor,const char * inputColor,const TransformedCoordsArray & coords,const TextureSamplerArray & samplers)63 void emitCode(GrGLFPBuilder* builder,
64 const GrFragmentProcessor& fp,
65 const char* outputColor,
66 const char* inputColor,
67 const TransformedCoordsArray& coords,
68 const TextureSamplerArray& samplers) override {
69 GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
70 fsBuilder->codeAppend("vec4 bgColor = ");
71 fsBuilder->appendTextureLookup(samplers[0], coords[0].c_str(), coords[0].getType());
72 fsBuilder->codeAppendf(";");
73 const char* dstColor = "bgColor";
74
75 fKUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
76 kVec4f_GrSLType, kDefault_GrSLPrecision,
77 "k");
78 const char* kUni = builder->getUniformCStr(fKUni);
79
80 add_arithmetic_code(fsBuilder, inputColor, dstColor, outputColor, kUni, fEnforcePMColor);
81 }
82
setData(const GrGLProgramDataManager & pdman,const GrProcessor & proc)83 void setData(const GrGLProgramDataManager& pdman, const GrProcessor& proc) override {
84 const GrArithmeticFP& arith = proc.cast<GrArithmeticFP>();
85 pdman.set4f(fKUni, arith.k1(), arith.k2(), arith.k3(), arith.k4());
86 fEnforcePMColor = arith.enforcePMColor();
87 }
88
GenKey(const GrProcessor & proc,const GrGLSLCaps & caps,GrProcessorKeyBuilder * b)89 static void GenKey(const GrProcessor& proc, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) {
90 const GrArithmeticFP& arith = proc.cast<GrArithmeticFP>();
91 uint32_t key = arith.enforcePMColor() ? 1 : 0;
92 b->add32(key);
93 }
94
95 private:
96 GrGLProgramDataManager::UniformHandle fKUni;
97 bool fEnforcePMColor;
98
99 typedef GrGLFragmentProcessor INHERITED;
100 };
101
102 ///////////////////////////////////////////////////////////////////////////////
103
GrArithmeticFP(float k1,float k2,float k3,float k4,bool enforcePMColor,GrTexture * background)104 GrArithmeticFP::GrArithmeticFP(float k1, float k2, float k3, float k4,
105 bool enforcePMColor, GrTexture* background)
106 : fK1(k1), fK2(k2), fK3(k3), fK4(k4), fEnforcePMColor(enforcePMColor) {
107 this->initClassID<GrArithmeticFP>();
108
109 SkASSERT(background);
110
111 fBackgroundTransform.reset(kLocal_GrCoordSet, background,
112 GrTextureParams::kNone_FilterMode);
113 this->addCoordTransform(&fBackgroundTransform);
114 fBackgroundAccess.reset(background);
115 this->addTextureAccess(&fBackgroundAccess);
116 }
117
getGLProcessorKey(const GrGLSLCaps & caps,GrProcessorKeyBuilder * b) const118 void GrArithmeticFP::getGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const {
119 GLArithmeticFP::GenKey(*this, caps, b);
120 }
121
createGLInstance() const122 GrGLFragmentProcessor* GrArithmeticFP::createGLInstance() const {
123 return SkNEW_ARGS(GLArithmeticFP, (*this));
124 }
125
onIsEqual(const GrFragmentProcessor & fpBase) const126 bool GrArithmeticFP::onIsEqual(const GrFragmentProcessor& fpBase) const {
127 const GrArithmeticFP& fp = fpBase.cast<GrArithmeticFP>();
128 return fK1 == fp.fK1 &&
129 fK2 == fp.fK2 &&
130 fK3 == fp.fK3 &&
131 fK4 == fp.fK4 &&
132 fEnforcePMColor == fp.fEnforcePMColor;
133 }
134
onComputeInvariantOutput(GrInvariantOutput * inout) const135 void GrArithmeticFP::onComputeInvariantOutput(GrInvariantOutput* inout) const {
136 // TODO: optimize this
137 inout->setToUnknown(GrInvariantOutput::kWill_ReadInput);
138 }
139
140 ///////////////////////////////////////////////////////////////////////////////
141
TestCreate(SkRandom * rand,GrContext *,const GrDrawTargetCaps &,GrTexture * textures[])142 GrFragmentProcessor* GrArithmeticFP::TestCreate(SkRandom* rand,
143 GrContext*,
144 const GrDrawTargetCaps&,
145 GrTexture* textures[]) {
146 float k1 = rand->nextF();
147 float k2 = rand->nextF();
148 float k3 = rand->nextF();
149 float k4 = rand->nextF();
150 bool enforcePMColor = rand->nextBool();
151
152 return SkNEW_ARGS(GrArithmeticFP, (k1, k2, k3, k4, enforcePMColor, textures[0]));
153 }
154
155 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrArithmeticFP);
156
157 ///////////////////////////////////////////////////////////////////////////////
158 // Xfer Processor
159 ///////////////////////////////////////////////////////////////////////////////
160
161 class ArithmeticXP : public GrXferProcessor {
162 public:
Create(float k1,float k2,float k3,float k4,bool enforcePMColor,const GrDeviceCoordTexture * dstCopy,bool willReadDstColor)163 static GrXferProcessor* Create(float k1, float k2, float k3, float k4, bool enforcePMColor,
164 const GrDeviceCoordTexture* dstCopy,
165 bool willReadDstColor) {
166 return SkNEW_ARGS(ArithmeticXP, (k1, k2, k3, k4, enforcePMColor, dstCopy,
167 willReadDstColor));
168 }
169
~ArithmeticXP()170 ~ArithmeticXP() override {};
171
name() const172 const char* name() const override { return "Arithmetic"; }
173
174 GrGLXferProcessor* createGLInstance() const override;
175
hasSecondaryOutput() const176 bool hasSecondaryOutput() const override { return false; }
177
k1() const178 float k1() const { return fK1; }
k2() const179 float k2() const { return fK2; }
k3() const180 float k3() const { return fK3; }
k4() const181 float k4() const { return fK4; }
enforcePMColor() const182 bool enforcePMColor() const { return fEnforcePMColor; }
183
184 private:
185 ArithmeticXP(float k1, float k2, float k3, float k4, bool enforcePMColor,
186 const GrDeviceCoordTexture* dstCopy, bool willReadDstColor);
187
188 GrXferProcessor::OptFlags onGetOptimizations(const GrProcOptInfo& colorPOI,
189 const GrProcOptInfo& coveragePOI,
190 bool doesStencilWrite,
191 GrColor* overrideColor,
192 const GrDrawTargetCaps& caps) override;
193
194 void onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override;
195
onIsEqual(const GrXferProcessor & xpBase) const196 bool onIsEqual(const GrXferProcessor& xpBase) const override {
197 const ArithmeticXP& xp = xpBase.cast<ArithmeticXP>();
198 if (fK1 != xp.fK1 ||
199 fK2 != xp.fK2 ||
200 fK3 != xp.fK3 ||
201 fK4 != xp.fK4 ||
202 fEnforcePMColor != xp.fEnforcePMColor) {
203 return false;
204 }
205 return true;
206 }
207
208 float fK1, fK2, fK3, fK4;
209 bool fEnforcePMColor;
210
211 typedef GrXferProcessor INHERITED;
212 };
213
214 ///////////////////////////////////////////////////////////////////////////////
215
216 class GLArithmeticXP : public GrGLXferProcessor {
217 public:
GLArithmeticXP(const GrProcessor &)218 GLArithmeticXP(const GrProcessor&)
219 : fEnforcePMColor(true) {
220 }
221
~GLArithmeticXP()222 ~GLArithmeticXP() override {}
223
GenKey(const GrProcessor & processor,const GrGLSLCaps & caps,GrProcessorKeyBuilder * b)224 static void GenKey(const GrProcessor& processor, const GrGLSLCaps& caps,
225 GrProcessorKeyBuilder* b) {
226 const ArithmeticXP& arith = processor.cast<ArithmeticXP>();
227 uint32_t key = arith.enforcePMColor() ? 1 : 0;
228 b->add32(key);
229 }
230
231 private:
onEmitCode(const EmitArgs & args)232 void onEmitCode(const EmitArgs& args) override {
233 GrGLXPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
234
235 const char* dstColor = fsBuilder->dstColor();
236
237 fKUni = args.fPB->addUniform(GrGLProgramBuilder::kFragment_Visibility,
238 kVec4f_GrSLType, kDefault_GrSLPrecision,
239 "k");
240 const char* kUni = args.fPB->getUniformCStr(fKUni);
241
242 add_arithmetic_code(fsBuilder, args.fInputColor, dstColor, args.fOutputPrimary, kUni,
243 fEnforcePMColor);
244
245 fsBuilder->codeAppendf("%s = %s * %s + (vec4(1.0) - %s) * %s;",
246 args.fOutputPrimary, args.fOutputPrimary, args.fInputCoverage,
247 args.fInputCoverage, dstColor);
248 }
249
onSetData(const GrGLProgramDataManager & pdman,const GrXferProcessor & processor)250 void onSetData(const GrGLProgramDataManager& pdman,
251 const GrXferProcessor& processor) override {
252 const ArithmeticXP& arith = processor.cast<ArithmeticXP>();
253 pdman.set4f(fKUni, arith.k1(), arith.k2(), arith.k3(), arith.k4());
254 fEnforcePMColor = arith.enforcePMColor();
255 };
256
257 GrGLProgramDataManager::UniformHandle fKUni;
258 bool fEnforcePMColor;
259
260 typedef GrGLXferProcessor INHERITED;
261 };
262
263 ///////////////////////////////////////////////////////////////////////////////
264
ArithmeticXP(float k1,float k2,float k3,float k4,bool enforcePMColor,const GrDeviceCoordTexture * dstCopy,bool willReadDstColor)265 ArithmeticXP::ArithmeticXP(float k1, float k2, float k3, float k4, bool enforcePMColor,
266 const GrDeviceCoordTexture* dstCopy, bool willReadDstColor)
267 : INHERITED(dstCopy, willReadDstColor)
268 , fK1(k1)
269 , fK2(k2)
270 , fK3(k3)
271 , fK4(k4)
272 , fEnforcePMColor(enforcePMColor) {
273 this->initClassID<ArithmeticXP>();
274 }
275
onGetGLProcessorKey(const GrGLSLCaps & caps,GrProcessorKeyBuilder * b) const276 void ArithmeticXP::onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const {
277 GLArithmeticXP::GenKey(*this, caps, b);
278 }
279
createGLInstance() const280 GrGLXferProcessor* ArithmeticXP::createGLInstance() const {
281 return SkNEW_ARGS(GLArithmeticXP, (*this));
282 }
283
onGetOptimizations(const GrProcOptInfo & colorPOI,const GrProcOptInfo & coveragePOI,bool doesStencilWrite,GrColor * overrideColor,const GrDrawTargetCaps & caps)284 GrXferProcessor::OptFlags ArithmeticXP::onGetOptimizations(const GrProcOptInfo& colorPOI,
285 const GrProcOptInfo& coveragePOI,
286 bool doesStencilWrite,
287 GrColor* overrideColor,
288 const GrDrawTargetCaps& caps) {
289 return GrXferProcessor::kNone_Opt;
290 }
291
292 ///////////////////////////////////////////////////////////////////////////////
293
GrArithmeticXPFactory(float k1,float k2,float k3,float k4,bool enforcePMColor)294 GrArithmeticXPFactory::GrArithmeticXPFactory(float k1, float k2, float k3, float k4,
295 bool enforcePMColor)
296 : fK1(k1), fK2(k2), fK3(k3), fK4(k4), fEnforcePMColor(enforcePMColor) {
297 this->initClassID<GrArithmeticXPFactory>();
298 }
299
300 GrXferProcessor*
onCreateXferProcessor(const GrDrawTargetCaps & caps,const GrProcOptInfo & colorPOI,const GrProcOptInfo & coveragePOI,const GrDeviceCoordTexture * dstCopy) const301 GrArithmeticXPFactory::onCreateXferProcessor(const GrDrawTargetCaps& caps,
302 const GrProcOptInfo& colorPOI,
303 const GrProcOptInfo& coveragePOI,
304 const GrDeviceCoordTexture* dstCopy) const {
305 return ArithmeticXP::Create(fK1, fK2, fK3, fK4, fEnforcePMColor, dstCopy,
306 this->willReadDstColor(caps, colorPOI, coveragePOI));
307 }
308
309
getInvariantOutput(const GrProcOptInfo & colorPOI,const GrProcOptInfo & coveragePOI,GrXPFactory::InvariantOutput * output) const310 void GrArithmeticXPFactory::getInvariantOutput(const GrProcOptInfo& colorPOI,
311 const GrProcOptInfo& coveragePOI,
312 GrXPFactory::InvariantOutput* output) const {
313 output->fWillBlendWithDst = true;
314
315 // TODO: We could try to optimize this more. For example if we have solid coverage and fK1 and
316 // fK3 are zero, then we won't be blending the color with dst at all so we can know what the
317 // output color is (up to the valid color components passed in).
318 output->fBlendedColorFlags = 0;
319 }
320
321 GR_DEFINE_XP_FACTORY_TEST(GrArithmeticXPFactory);
322
TestCreate(SkRandom * random,GrContext *,const GrDrawTargetCaps &,GrTexture * [])323 GrXPFactory* GrArithmeticXPFactory::TestCreate(SkRandom* random,
324 GrContext*,
325 const GrDrawTargetCaps&,
326 GrTexture*[]) {
327 float k1 = random->nextF();
328 float k2 = random->nextF();
329 float k3 = random->nextF();
330 float k4 = random->nextF();
331 bool enforcePMColor = random->nextBool();
332
333 return GrArithmeticXPFactory::Create(k1, k2, k3, k4, enforcePMColor);
334 }
335
336 #endif
337