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 #ifndef GrOp_DEFINED
9 #define GrOp_DEFINED
10 
11 #include "../private/SkAtomics.h"
12 #include "GrGpuResource.h"
13 #include "GrNonAtomicRef.h"
14 #include "GrXferProcessor.h"
15 #include "SkMatrix.h"
16 #include "SkRect.h"
17 #include "SkString.h"
18 
19 #include <new>
20 
21 class GrCaps;
22 class GrGpuCommandBuffer;
23 class GrOpFlushState;
24 
25 /**
26  * GrOp is the base class for all Ganesh deferred GPU operations. To facilitate reordering and to
27  * minimize draw calls, Ganesh does not generate geometry inline with draw calls. Instead, it
28  * captures the arguments to the draw and then generates the geometry when flushing. This gives GrOp
29  * subclasses complete freedom to decide how/when to combine in order to produce fewer draw calls
30  * and minimize state changes.
31  *
32  * Ops of the same subclass may be merged using combineIfPossible. When two ops merge, one
33  * takes on the union of the data and the other is left empty. The merged op becomes responsible
34  * for drawing the data from both the original ops.
35  *
36  * If there are any possible optimizations which might require knowing more about the full state of
37  * the draw, e.g. whether or not the GrOp is allowed to tweak alpha for coverage, then this
38  * information will be communicated to the GrOp prior to geometry generation.
39  *
40  * The bounds of the op must contain all the vertices in device space *irrespective* of the clip.
41  * The bounds are used in determining which clip elements must be applied and thus the bounds cannot
42  * in turn depend upon the clip.
43  */
44 #define GR_OP_SPEW 0
45 #if GR_OP_SPEW
46     #define GrOP_SPEW(code) code
47     #define GrOP_INFO(...) SkDebugf(__VA_ARGS__)
48 #else
49     #define GrOP_SPEW(code)
50     #define GrOP_INFO(...)
51 #endif
52 
53 // A helper macro to generate a class static id
54 #define DEFINE_OP_CLASS_ID \
55     static uint32_t ClassID() { \
56         static uint32_t kClassID = GenOpClassID(); \
57         return kClassID; \
58     }
59 
60 class GrOp : private SkNoncopyable {
61 public:
62     GrOp(uint32_t classID);
63     virtual ~GrOp();
64 
65     virtual const char* name() const = 0;
66 
combineIfPossible(GrOp * that,const GrCaps & caps)67     bool combineIfPossible(GrOp* that, const GrCaps& caps) {
68         if (this->classID() != that->classID()) {
69             return false;
70         }
71 
72         return this->onCombineIfPossible(that, caps);
73     }
74 
bounds()75     const SkRect& bounds() const {
76         SkASSERT(kUninitialized_BoundsFlag != fBoundsFlags);
77         return fBounds;
78     }
79 
setClippedBounds(const SkRect & clippedBounds)80     void setClippedBounds(const SkRect& clippedBounds) {
81         fBounds = clippedBounds;
82         // The clipped bounds already incorporate any effect of the bounds flags.
83         fBoundsFlags = 0;
84     }
85 
hasAABloat()86     bool hasAABloat() const {
87         SkASSERT(fBoundsFlags != kUninitialized_BoundsFlag);
88         return SkToBool(fBoundsFlags & kAABloat_BoundsFlag);
89     }
90 
hasZeroArea()91     bool hasZeroArea() const {
92         SkASSERT(fBoundsFlags != kUninitialized_BoundsFlag);
93         return SkToBool(fBoundsFlags & kZeroArea_BoundsFlag);
94     }
95 
96     void* operator new(size_t size);
97     void operator delete(void* target);
98 
new(size_t size,void * placement)99     void* operator new(size_t size, void* placement) {
100         return ::operator new(size, placement);
101     }
delete(void * target,void * placement)102     void operator delete(void* target, void* placement) {
103         ::operator delete(target, placement);
104     }
105 
106     /**
107      * Helper for safely down-casting to a GrOp subclass
108      */
cast()109     template <typename T> const T& cast() const {
110         SkASSERT(T::ClassID() == this->classID());
111         return *static_cast<const T*>(this);
112     }
113 
cast()114     template <typename T> T* cast() {
115         SkASSERT(T::ClassID() == this->classID());
116         return static_cast<T*>(this);
117     }
118 
classID()119     uint32_t classID() const { SkASSERT(kIllegalOpID != fClassID); return fClassID; }
120 
121     // We lazily initialize the uniqueID because currently the only user is GrAuditTrail
uniqueID()122     uint32_t uniqueID() const {
123         if (kIllegalOpID == fUniqueID) {
124             fUniqueID = GenOpID();
125         }
126         return fUniqueID;
127     }
128 
129     /**
130      * This is called to notify the op that it has been recorded into a GrOpList. Ops can use this
131      * to begin preparations for the flush of the op list. Note that the op still may either be
132      * combined into another op or have another op combined into it via combineIfPossible() after
133      * this call is made.
134      */
wasRecorded()135     virtual void wasRecorded() {}
136 
137     /**
138      * Called prior to executing. The op should perform any resource creation or data transfers
139      * necessary before execute() is called.
140      */
prepare(GrOpFlushState * state)141     void prepare(GrOpFlushState* state) { this->onPrepare(state); }
142 
143     /** Issues the op's commands to GrGpu. */
execute(GrOpFlushState * state)144     void execute(GrOpFlushState* state) { this->onExecute(state); }
145 
146     /** Used for spewing information about ops when debugging. */
dumpInfo()147     virtual SkString dumpInfo() const {
148         SkString string;
149         string.appendf("OpBounds: [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n",
150                        fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
151         return string;
152     }
153 
154 protected:
155     /**
156      * Indicates that the op will produce geometry that extends beyond its bounds for the
157      * purpose of ensuring that the fragment shader runs on partially covered pixels for
158      * non-MSAA antialiasing.
159      */
160     enum class HasAABloat {
161         kYes,
162         kNo
163     };
164     /**
165      * Indicates that the geometry represented by the op has zero area (e.g. it is hairline or
166      * points).
167      */
168     enum class IsZeroArea {
169         kYes,
170         kNo
171     };
setBounds(const SkRect & newBounds,HasAABloat aabloat,IsZeroArea zeroArea)172     void setBounds(const SkRect& newBounds, HasAABloat aabloat, IsZeroArea zeroArea) {
173         fBounds = newBounds;
174         this->setBoundsFlags(aabloat, zeroArea);
175     }
setTransformedBounds(const SkRect & srcBounds,const SkMatrix & m,HasAABloat aabloat,IsZeroArea zeroArea)176     void setTransformedBounds(const SkRect& srcBounds, const SkMatrix& m,
177                               HasAABloat aabloat, IsZeroArea zeroArea) {
178         m.mapRect(&fBounds, srcBounds);
179         this->setBoundsFlags(aabloat, zeroArea);
180     }
181 
joinBounds(const GrOp & that)182     void joinBounds(const GrOp& that) {
183         if (that.hasAABloat()) {
184             fBoundsFlags |= kAABloat_BoundsFlag;
185         }
186         if (that.hasZeroArea()) {
187             fBoundsFlags |= kZeroArea_BoundsFlag;
188         }
189         return fBounds.joinPossiblyEmptyRect(that.fBounds);
190     }
191 
replaceBounds(const GrOp & that)192     void replaceBounds(const GrOp& that) {
193         fBounds = that.fBounds;
194         fBoundsFlags = that.fBoundsFlags;
195     }
196 
GenOpClassID()197     static uint32_t GenOpClassID() { return GenID(&gCurrOpClassID); }
198 
199 private:
200     virtual bool onCombineIfPossible(GrOp*, const GrCaps& caps) = 0;
201 
202     virtual void onPrepare(GrOpFlushState*) = 0;
203     virtual void onExecute(GrOpFlushState*) = 0;
204 
GenID(int32_t * idCounter)205     static uint32_t GenID(int32_t* idCounter) {
206         // The atomic inc returns the old value not the incremented value. So we add
207         // 1 to the returned value.
208         uint32_t id = static_cast<uint32_t>(sk_atomic_inc(idCounter)) + 1;
209         if (!id) {
210             SkFAIL("This should never wrap as it should only be called once for each GrOp "
211                    "subclass.");
212         }
213         return id;
214     }
215 
setBoundsFlags(HasAABloat aabloat,IsZeroArea zeroArea)216     void setBoundsFlags(HasAABloat aabloat, IsZeroArea zeroArea) {
217         fBoundsFlags = 0;
218         fBoundsFlags |= (HasAABloat::kYes == aabloat) ? kAABloat_BoundsFlag : 0;
219         fBoundsFlags |= (IsZeroArea ::kYes == zeroArea) ? kZeroArea_BoundsFlag : 0;
220     }
221 
222     enum {
223         kIllegalOpID = 0,
224     };
225 
226     enum BoundsFlags {
227         kAABloat_BoundsFlag                     = 0x1,
228         kZeroArea_BoundsFlag                    = 0x2,
229         SkDEBUGCODE(kUninitialized_BoundsFlag   = 0x4)
230     };
231 
232     const uint16_t                      fClassID;
233     uint16_t                            fBoundsFlags;
234 
GenOpID()235     static uint32_t GenOpID() { return GenID(&gCurrOpUniqueID); }
236     mutable uint32_t                    fUniqueID;
237     SkRect                              fBounds;
238 
239     static int32_t                      gCurrOpUniqueID;
240     static int32_t                      gCurrOpClassID;
241 };
242 
243 #endif
244