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