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
21class GrCaps;
22class GrGpuCommandBuffer;
23class 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
60class GrOp : private SkNoncopyable {
61public:
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
154protected:
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
199private:
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