Home | History | Annotate | Download | only in ops
      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 
     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 
     75     const SkRect& bounds() const {
     76         SkASSERT(kUninitialized_BoundsFlag != fBoundsFlags);
     77         return fBounds;
     78     }
     79 
     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 
     86     bool hasAABloat() const {
     87         SkASSERT(fBoundsFlags != kUninitialized_BoundsFlag);
     88         return SkToBool(fBoundsFlags & kAABloat_BoundsFlag);
     89     }
     90 
     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 
     99     void* operator new(size_t size, void* placement) {
    100         return ::operator new(size, placement);
    101     }
    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      */
    109     template <typename T> const T& cast() const {
    110         SkASSERT(T::ClassID() == this->classID());
    111         return *static_cast<const T*>(this);
    112     }
    113 
    114     template <typename T> T* cast() {
    115         SkASSERT(T::ClassID() == this->classID());
    116         return static_cast<T*>(this);
    117     }
    118 
    119     uint32_t classID() const { SkASSERT(kIllegalOpID != fClassID); return fClassID; }
    120 
    121     // We lazily initialize the uniqueID because currently the only user is GrAuditTrail
    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      */
    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      */
    141     void prepare(GrOpFlushState* state) { this->onPrepare(state); }
    142 
    143     /** Issues the op's commands to GrGpu. */
    144     void execute(GrOpFlushState* state) { this->onExecute(state); }
    145 
    146     /** Used for spewing information about ops when debugging. */
    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     };
    172     void setBounds(const SkRect& newBounds, HasAABloat aabloat, IsZeroArea zeroArea) {
    173         fBounds = newBounds;
    174         this->setBoundsFlags(aabloat, zeroArea);
    175     }
    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 
    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 
    192     void replaceBounds(const GrOp& that) {
    193         fBounds = that.fBounds;
    194         fBoundsFlags = that.fBoundsFlags;
    195     }
    196 
    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 
    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 
    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 
    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