• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2014 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 #ifndef GrFragmentProcessor_DEFINED
9 #define GrFragmentProcessor_DEFINED
10 
11 #include "src/gpu/GrProcessor.h"
12 #include "src/gpu/ops/GrOp.h"
13 
14 class GrCoordTransform;
15 class GrGLSLFragmentProcessor;
16 class GrPaint;
17 class GrPipeline;
18 class GrProcessorKeyBuilder;
19 class GrShaderCaps;
20 class GrSwizzle;
21 
22 /** Provides custom fragment shader code. Fragment processors receive an input color (half4) and
23     produce an output color. They may reference textures and uniforms. They may use
24     GrCoordTransforms to receive a transformation of the local coordinates that map from local space
25     to the fragment being processed.
26  */
27 class GrFragmentProcessor : public GrProcessor {
28 public:
29     class TextureSampler;
30 
31     /**
32     *  In many instances (e.g. SkShader::asFragmentProcessor() implementations) it is desirable to
33     *  only consider the input color's alpha. However, there is a competing desire to have reusable
34     *  GrFragmentProcessor subclasses that can be used in other scenarios where the entire input
35     *  color is considered. This function exists to filter the input color and pass it to a FP. It
36     *  does so by returning a parent FP that multiplies the passed in FPs output by the parent's
37     *  input alpha. The passed in FP will not receive an input color.
38     */
39     static std::unique_ptr<GrFragmentProcessor> MulChildByInputAlpha(
40             std::unique_ptr<GrFragmentProcessor> child);
41 
42     /**
43      *  Like MulChildByInputAlpha(), but reverses the sense of src and dst. In this case, return
44      *  the input modulated by the child's alpha. The passed in FP will not receive an input color.
45      *
46      *  output = input * child.a
47      */
48     static std::unique_ptr<GrFragmentProcessor> MulInputByChildAlpha(
49             std::unique_ptr<GrFragmentProcessor> child);
50 
51     /**
52      *  This assumes that the input color to the returned processor will be unpremul and that the
53      *  passed processor (which becomes the returned processor's child) produces a premul output.
54      *  The result of the returned processor is a premul of its input color modulated by the child
55      *  processor's premul output.
56      */
57     static std::unique_ptr<GrFragmentProcessor> MakeInputPremulAndMulByOutput(
58             std::unique_ptr<GrFragmentProcessor>);
59 
60     /**
61      *  Returns a parent fragment processor that adopts the passed fragment processor as a child.
62      *  The parent will ignore its input color and instead feed the passed in color as input to the
63      *  child.
64      */
65     static std::unique_ptr<GrFragmentProcessor> OverrideInput(std::unique_ptr<GrFragmentProcessor>,
66                                                               const SkPMColor4f&,
67                                                               bool useUniform = true);
68 
69     /**
70      *  Returns a fragment processor that premuls the input before calling the passed in fragment
71      *  processor.
72      */
73     static std::unique_ptr<GrFragmentProcessor> PremulInput(std::unique_ptr<GrFragmentProcessor>);
74 
75     /**
76      *  Returns a fragment processor that calls the passed in fragment processor, and then swizzles
77      *  the output.
78      */
79     static std::unique_ptr<GrFragmentProcessor> SwizzleOutput(std::unique_ptr<GrFragmentProcessor>,
80                                                               const GrSwizzle&);
81 
82     /**
83      * Returns a fragment processor that runs the passed in array of fragment processors in a
84      * series. The original input is passed to the first, the first's output is passed to the
85      * second, etc. The output of the returned processor is the output of the last processor of the
86      * series.
87      *
88      * The array elements with be moved.
89      */
90     static std::unique_ptr<GrFragmentProcessor> RunInSeries(std::unique_ptr<GrFragmentProcessor>*,
91                                                             int cnt);
92 
93     /**
94      * Makes a copy of this fragment processor that draws equivalently to the original.
95      * If the processor has child processors they are cloned as well.
96      */
97     virtual std::unique_ptr<GrFragmentProcessor> clone() const = 0;
98 
99     GrGLSLFragmentProcessor* createGLSLInstance() const;
100 
getGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b)101     void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const {
102         this->onGetGLSLProcessorKey(caps, b);
103         for (int i = 0; i < fChildProcessors.count(); ++i) {
104             fChildProcessors[i]->getGLSLProcessorKey(caps, b);
105         }
106     }
107 
numTextureSamplers()108     int numTextureSamplers() const { return fTextureSamplerCnt; }
109     const TextureSampler& textureSampler(int i) const;
110 
numCoordTransforms()111     int numCoordTransforms() const { return fCoordTransforms.count(); }
112 
113     /** Returns the coordinate transformation at index. index must be valid according to
114         numTransforms(). */
coordTransform(int index)115     const GrCoordTransform& coordTransform(int index) const { return *fCoordTransforms[index]; }
116 
coordTransforms()117     const SkTArray<const GrCoordTransform*, true>& coordTransforms() const {
118         return fCoordTransforms;
119     }
120 
numChildProcessors()121     int numChildProcessors() const { return fChildProcessors.count(); }
122 
childProcessor(int index)123     const GrFragmentProcessor& childProcessor(int index) const { return *fChildProcessors[index]; }
124 
SkDEBUGCODE(bool isInstantiated ()const;)125     SkDEBUGCODE(bool isInstantiated() const;)
126 
127     /** Do any of the coordtransforms for this processor require local coords? */
128     bool usesLocalCoords() const { return SkToBool(fFlags & kUsesLocalCoords_Flag); }
129 
130     /**
131      * A GrDrawOp may premultiply its antialiasing coverage into its GrGeometryProcessor's color
132      * output under the following scenario:
133      *   * all the color fragment processors report true to this query,
134      *   * all the coverage fragment processors report true to this query,
135      *   * the blend mode arithmetic allows for it it.
136      * To be compatible a fragment processor's output must be a modulation of its input color or
137      * alpha with a computed premultiplied color or alpha that is in 0..1 range. The computed color
138      * or alpha that is modulated against the input cannot depend on the input's alpha. The computed
139      * value cannot depend on the input's color channels unless it unpremultiplies the input color
140      * channels by the input alpha.
141      */
compatibleWithCoverageAsAlpha()142     bool compatibleWithCoverageAsAlpha() const {
143         return SkToBool(fFlags & kCompatibleWithCoverageAsAlpha_OptimizationFlag);
144     }
145 
146     /**
147      * If this is true then all opaque input colors to the processor produce opaque output colors.
148      */
preservesOpaqueInput()149     bool preservesOpaqueInput() const {
150         return SkToBool(fFlags & kPreservesOpaqueInput_OptimizationFlag);
151     }
152 
153     /**
154      * Tests whether given a constant input color the processor produces a constant output color
155      * (for all fragments). If true outputColor will contain the constant color produces for
156      * inputColor.
157      */
hasConstantOutputForConstantInput(SkPMColor4f inputColor,SkPMColor4f * outputColor)158     bool hasConstantOutputForConstantInput(SkPMColor4f inputColor, SkPMColor4f* outputColor) const {
159         if (fFlags & kConstantOutputForConstantInput_OptimizationFlag) {
160             *outputColor = this->constantOutputForConstantInput(inputColor);
161             return true;
162         }
163         return false;
164     }
hasConstantOutputForConstantInput()165     bool hasConstantOutputForConstantInput() const {
166         return SkToBool(fFlags & kConstantOutputForConstantInput_OptimizationFlag);
167     }
168 
169     /** Returns true if this and other processor conservatively draw identically. It can only return
170         true when the two processor are of the same subclass (i.e. they return the same object from
171         from getFactory()).
172 
173         A return value of true from isEqual() should not be used to test whether the processor would
174         generate the same shader code. To test for identical code generation use getGLSLProcessorKey
175      */
176     bool isEqual(const GrFragmentProcessor& that) const;
177 
178     /**
179      * Pre-order traversal of a FP hierarchy, or of the forest of FPs in a GrPipeline. In the latter
180      * case the tree rooted at each FP in the GrPipeline is visited successively.
181      */
182     class Iter : public SkNoncopyable {
183     public:
Iter(const GrFragmentProcessor * fp)184         explicit Iter(const GrFragmentProcessor* fp) { fFPStack.push_back(fp); }
185         explicit Iter(const GrPipeline& pipeline);
186         explicit Iter(const GrPaint&);
187         const GrFragmentProcessor* next();
188 
189     private:
190         SkSTArray<4, const GrFragmentProcessor*, true> fFPStack;
191     };
192 
193     /**
194      * Iterates over all the Ts owned by a GrFragmentProcessor and its children or over all the Ts
195      * owned by the forest of GrFragmentProcessors in a GrPipeline. FPs are visited in the same
196      * order as Iter and each of an FP's Ts are visited in order.
197      */
198     template <typename T, int (GrFragmentProcessor::*COUNT)() const,
199               const T& (GrFragmentProcessor::*GET)(int)const>
200     class FPItemIter : public SkNoncopyable {
201     public:
FPItemIter(const GrFragmentProcessor * fp)202         explicit FPItemIter(const GrFragmentProcessor* fp)
203                 : fCurrFP(nullptr)
204                 , fCTIdx(0)
205                 , fFPIter(fp) {
206             fCurrFP = fFPIter.next();
207         }
FPItemIter(const GrPipeline & pipeline)208         explicit FPItemIter(const GrPipeline& pipeline)
209                 : fCurrFP(nullptr)
210                 , fCTIdx(0)
211                 , fFPIter(pipeline) {
212             fCurrFP = fFPIter.next();
213         }
214 
next()215         const T* next() {
216             if (!fCurrFP) {
217                 return nullptr;
218             }
219             while (fCTIdx == (fCurrFP->*COUNT)()) {
220                 fCTIdx = 0;
221                 fCurrFP = fFPIter.next();
222                 if (!fCurrFP) {
223                     return nullptr;
224                 }
225             }
226             return &(fCurrFP->*GET)(fCTIdx++);
227         }
228 
229     private:
230         const GrFragmentProcessor*  fCurrFP;
231         int                         fCTIdx;
232         GrFragmentProcessor::Iter   fFPIter;
233     };
234 
235     using CoordTransformIter = FPItemIter<GrCoordTransform,
236                                           &GrFragmentProcessor::numCoordTransforms,
237                                           &GrFragmentProcessor::coordTransform>;
238 
239     using TextureAccessIter = FPItemIter<TextureSampler,
240                                          &GrFragmentProcessor::numTextureSamplers,
241                                          &GrFragmentProcessor::textureSampler>;
242 
243     void visitProxies(const GrOp::VisitProxyFunc& func);
244 
245 protected:
246     enum OptimizationFlags : uint32_t {
247         kNone_OptimizationFlags,
248         kCompatibleWithCoverageAsAlpha_OptimizationFlag = 0x1,
249         kPreservesOpaqueInput_OptimizationFlag = 0x2,
250         kConstantOutputForConstantInput_OptimizationFlag = 0x4,
251         kAll_OptimizationFlags = kCompatibleWithCoverageAsAlpha_OptimizationFlag |
252                                  kPreservesOpaqueInput_OptimizationFlag |
253                                  kConstantOutputForConstantInput_OptimizationFlag
254     };
GR_DECL_BITFIELD_OPS_FRIENDS(OptimizationFlags)255     GR_DECL_BITFIELD_OPS_FRIENDS(OptimizationFlags)
256 
257     /**
258      * Can be used as a helper to decide which fragment processor OptimizationFlags should be set.
259      * This assumes that the subclass output color will be a modulation of the input color with a
260      * value read from a texture of the passed config and that the texture contains premultiplied
261      * color or alpha values that are in range.
262      *
263      * Since there are multiple ways in which a sampler may have its coordinates clamped or wrapped,
264      * callers must determine on their own if the sampling uses a decal strategy in any way, in
265      * which case the texture may become transparent regardless of the pixel config.
266      */
267     static OptimizationFlags ModulateForSamplerOptFlags(GrPixelConfig config, bool samplingDecal) {
268         if (samplingDecal) {
269             return kCompatibleWithCoverageAsAlpha_OptimizationFlag;
270         } else {
271             return ModulateForClampedSamplerOptFlags(config);
272         }
273     }
274 
275     // As above, but callers should somehow ensure or assert their sampler still uses clamping
ModulateForClampedSamplerOptFlags(GrPixelConfig config)276     static OptimizationFlags ModulateForClampedSamplerOptFlags(GrPixelConfig config) {
277         if (GrPixelConfigIsOpaque(config)) {
278             return kCompatibleWithCoverageAsAlpha_OptimizationFlag |
279                    kPreservesOpaqueInput_OptimizationFlag;
280         } else {
281             return kCompatibleWithCoverageAsAlpha_OptimizationFlag;
282         }
283     }
284 
GrFragmentProcessor(ClassID classID,OptimizationFlags optimizationFlags)285     GrFragmentProcessor(ClassID classID, OptimizationFlags optimizationFlags)
286             : INHERITED(classID)
287             , fFlags(optimizationFlags) {
288         SkASSERT((fFlags & ~kAll_OptimizationFlags) == 0);
289     }
290 
optimizationFlags()291     OptimizationFlags optimizationFlags() const {
292         return static_cast<OptimizationFlags>(kAll_OptimizationFlags & fFlags);
293     }
294 
295     /** Useful when you can't call fp->optimizationFlags() on a base class object from a subclass.*/
ProcessorOptimizationFlags(const GrFragmentProcessor * fp)296     static OptimizationFlags ProcessorOptimizationFlags(const GrFragmentProcessor* fp) {
297         return fp->optimizationFlags();
298     }
299 
300     /**
301      * This allows one subclass to access another subclass's implementation of
302      * constantOutputForConstantInput. It must only be called when
303      * hasConstantOutputForConstantInput() is known to be true.
304      */
ConstantOutputForConstantInput(const GrFragmentProcessor & fp,const SkPMColor4f & input)305     static SkPMColor4f ConstantOutputForConstantInput(const GrFragmentProcessor& fp,
306                                                       const SkPMColor4f& input) {
307         SkASSERT(fp.hasConstantOutputForConstantInput());
308         return fp.constantOutputForConstantInput(input);
309     }
310 
311     /**
312      * Fragment Processor subclasses call this from their constructor to register coordinate
313      * transformations. Coord transforms provide a mechanism for a processor to receive coordinates
314      * in their FS code. The matrix expresses a transformation from local space. For a given
315      * fragment the matrix will be applied to the local coordinate that maps to the fragment.
316      *
317      * When the transformation has perspective, the transformed coordinates will have
318      * 3 components. Otherwise they'll have 2.
319      *
320      * This must only be called from the constructor because GrProcessors are immutable. The
321      * processor subclass manages the lifetime of the transformations (this function only stores a
322      * pointer). The GrCoordTransform is typically a member field of the GrProcessor subclass.
323      *
324      * A processor subclass that has multiple methods of construction should always add its coord
325      * transforms in a consistent order. The non-virtual implementation of isEqual() automatically
326      * compares transforms and will assume they line up across the two processor instances.
327      */
328     void addCoordTransform(const GrCoordTransform*);
329 
330     /**
331      * FragmentProcessor subclasses call this from their constructor to register any child
332      * FragmentProcessors they have. This must be called AFTER all texture accesses and coord
333      * transforms have been added.
334      * This is for processors whose shader code will be composed of nested processors whose output
335      * colors will be combined somehow to produce its output color.  Registering these child
336      * processors will allow the ProgramBuilder to automatically handle their transformed coords and
337      * texture accesses and mangle their uniform and output color names.
338      */
339     int registerChildProcessor(std::unique_ptr<GrFragmentProcessor> child);
340 
setTextureSamplerCnt(int cnt)341     void setTextureSamplerCnt(int cnt) {
342         SkASSERT(cnt >= 0);
343         fTextureSamplerCnt = cnt;
344     }
345 
346     /**
347      * Helper for implementing onTextureSampler(). E.g.:
348      * return IthTexureSampler(i, fMyFirstSampler, fMySecondSampler, fMyThirdSampler);
349      */
350     template <typename... Args>
IthTextureSampler(int i,const TextureSampler & samp0,const Args &...samps)351     static const TextureSampler& IthTextureSampler(int i, const TextureSampler& samp0,
352                                                    const Args&... samps) {
353         return (0 == i) ? samp0 : IthTextureSampler(i - 1, samps...);
354     }
355     inline static const TextureSampler& IthTextureSampler(int i);
356 
357 private:
constantOutputForConstantInput(const SkPMColor4f &)358     virtual SkPMColor4f constantOutputForConstantInput(const SkPMColor4f& /* inputColor */) const {
359         SK_ABORT("Subclass must override this if advertising this optimization.");
360     }
361 
362     /** Returns a new instance of the appropriate *GL* implementation class
363         for the given GrFragmentProcessor; caller is responsible for deleting
364         the object. */
365     virtual GrGLSLFragmentProcessor* onCreateGLSLInstance() const = 0;
366 
367     /** Implemented using GLFragmentProcessor::GenKey as described in this class's comment. */
368     virtual void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const = 0;
369 
370     /**
371      * Subclass implements this to support isEqual(). It will only be called if it is known that
372      * the two processors are of the same subclass (i.e. they return the same object from
373      * getFactory()). The processor subclass should not compare its coord transforms as that will
374      * be performed automatically in the non-virtual isEqual().
375      */
376     virtual bool onIsEqual(const GrFragmentProcessor&) const = 0;
377 
onTextureSampler(int)378     virtual const TextureSampler& onTextureSampler(int) const { return IthTextureSampler(0); }
379 
380     bool hasSameTransforms(const GrFragmentProcessor&) const;
381 
382     enum PrivateFlags {
383         kFirstPrivateFlag = kAll_OptimizationFlags + 1,
384         kUsesLocalCoords_Flag = kFirstPrivateFlag,
385     };
386 
387     mutable uint32_t fFlags = 0;
388 
389     int fTextureSamplerCnt = 0;
390 
391     SkSTArray<4, const GrCoordTransform*, true> fCoordTransforms;
392 
393     SkSTArray<1, std::unique_ptr<GrFragmentProcessor>, true> fChildProcessors;
394 
395     typedef GrProcessor INHERITED;
396 };
397 
398 /**
399  * Used to represent a texture that is required by a GrFragmentProcessor. It holds a GrTextureProxy
400  * along with an associated GrSamplerState. TextureSamplers don't perform any coord manipulation to
401  * account for texture origin.
402  */
403 class GrFragmentProcessor::TextureSampler {
404 public:
405     TextureSampler() = default;
406 
407     /**
408      * This copy constructor is used by GrFragmentProcessor::clone() implementations. The copy
409      * always takes a new ref on the texture proxy as the new fragment processor will not yet be
410      * in pending execution state.
411      */
TextureSampler(const TextureSampler & that)412     explicit TextureSampler(const TextureSampler& that)
413             : fProxy(that.fProxy)
414             , fSamplerState(that.fSamplerState) {}
415 
416     TextureSampler(sk_sp<GrTextureProxy>, const GrSamplerState&);
417 
418     explicit TextureSampler(sk_sp<GrTextureProxy>,
419                             GrSamplerState::Filter = GrSamplerState::Filter::kNearest,
420                             GrSamplerState::WrapMode wrapXAndY = GrSamplerState::WrapMode::kClamp);
421 
422     TextureSampler& operator=(const TextureSampler&) = delete;
423 
424     void reset(sk_sp<GrTextureProxy>, const GrSamplerState&);
425     void reset(sk_sp<GrTextureProxy>,
426                GrSamplerState::Filter = GrSamplerState::Filter::kNearest,
427                GrSamplerState::WrapMode wrapXAndY = GrSamplerState::WrapMode::kClamp);
428 
429     bool operator==(const TextureSampler& that) const {
430         return this->proxy()->underlyingUniqueID() == that.proxy()->underlyingUniqueID() &&
431                fSamplerState == that.fSamplerState;
432     }
433 
434     bool operator!=(const TextureSampler& other) const { return !(*this == other); }
435 
SkDEBUGCODE(bool isInstantiated ()const{ return fProxy->isInstantiated(); })436     SkDEBUGCODE(bool isInstantiated() const { return fProxy->isInstantiated(); })
437 
438     // 'peekTexture' should only ever be called after a successful 'instantiate' call
439     GrTexture* peekTexture() const {
440         SkASSERT(fProxy->isInstantiated());
441         return fProxy->peekTexture();
442     }
443 
proxy()444     GrTextureProxy* proxy() const { return fProxy.get(); }
samplerState()445     const GrSamplerState& samplerState() const { return fSamplerState; }
swizzle()446     const GrSwizzle& swizzle() const { return this->proxy()->textureSwizzle(); }
447 
isInitialized()448     bool isInitialized() const { return SkToBool(fProxy.get()); }
449 
450 private:
451     sk_sp<GrTextureProxy> fProxy;
452     GrSamplerState        fSamplerState;
453 };
454 
455 //////////////////////////////////////////////////////////////////////////////
456 
IthTextureSampler(int i)457 const GrFragmentProcessor::TextureSampler& GrFragmentProcessor::IthTextureSampler(int i) {
458     SK_ABORT("Illegal texture sampler index");
459     static const TextureSampler kBogus;
460     return kBogus;
461 }
462 
463 GR_MAKE_BITFIELD_OPS(GrFragmentProcessor::OptimizationFlags)
464 
465 #endif
466