Flutter Engine
The Flutter Engine
GrOp.h
Go to the documentation of this file.
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
12#include "include/core/SkRect.h"
18#include <atomic>
19#include <new>
20
21class GrAppliedClip;
22class GrCaps;
23class GrDstProxyView;
24class GrOpFlushState;
25class GrOpsRenderPass;
26class GrPaint;
29
30/**
31 * GrOp is the base class for all Ganesh deferred GPU operations. To facilitate reordering and to
32 * minimize draw calls, Ganesh does not generate geometry inline with draw calls. Instead, it
33 * captures the arguments to the draw and then generates the geometry when flushing. This gives GrOp
34 * subclasses complete freedom to decide how/when to combine in order to produce fewer draw calls
35 * and minimize state changes.
36 *
37 * Ops of the same subclass may be merged or chained using combineIfPossible. When two ops merge,
38 * one takes on the union of the data and the other is left empty. The merged op becomes responsible
39 * for drawing the data from both the original ops. When ops are chained each op maintains its own
40 * data but they are linked in a list and the head op becomes responsible for executing the work for
41 * the chain.
42 *
43 * It is required that chainability is transitive. Moreover, if op A is able to merge with B then
44 * it must be the case that any op that can chain with A will either merge or chain with any op
45 * that can chain to B.
46 *
47 * The bounds of the op must contain all the vertices in device space *irrespective* of the clip.
48 * The bounds are used in determining which clip elements must be applied and thus the bounds cannot
49 * in turn depend upon the clip.
50 */
51#define GR_OP_SPEW 0
52#if GR_OP_SPEW
53 #define GrOP_SPEW(code) code
54 #define GrOP_INFO(...) SkDebugf(__VA_ARGS__)
55#else
56 #define GrOP_SPEW(code)
57 #define GrOP_INFO(...)
58#endif
59
60// Print out op information at flush time
61#define GR_FLUSH_TIME_OP_SPEW 0
62
63// A helper macro to generate a class static id
64#define DEFINE_OP_CLASS_ID \
65 static uint32_t ClassID() { \
66 static uint32_t kClassID = GenOpClassID(); \
67 return kClassID; \
68 }
69
70class GrOp : private SkNoncopyable {
71public:
72 using Owner = std::unique_ptr<GrOp>;
73
74 template<typename Op, typename... Args>
75 static Owner Make(GrRecordingContext* context, Args&&... args) {
76 return Owner{new Op(std::forward<Args>(args)...)};
77 }
78
79 template<typename Op, typename... Args>
81 GrRecordingContext* context, const SkPMColor4f& color,
82 GrPaint&& paint, Args&&... args);
83
84 template<typename Op, typename... Args>
86 GrRecordingContext* context, size_t extraSize, Args&&... args) {
87 void* bytes = ::operator new(sizeof(Op) + extraSize);
88 return Owner{new (bytes) Op(std::forward<Args>(args)...)};
89 }
90
91 virtual ~GrOp() = default;
92
93 virtual const char* name() const = 0;
94
95 virtual void visitProxies(const GrVisitProxyFunc&) const {
96 // This default implementation assumes the op has no proxies
97 }
98
99 enum class CombineResult {
100 /**
101 * The op that combineIfPossible was called on now represents its own work plus that of
102 * the passed op. The passed op should be destroyed without being flushed. Currently it
103 * is not legal to merge an op passed to combineIfPossible() the passed op is already in a
104 * chain (though the op on which combineIfPossible() was called may be).
105 */
106 kMerged,
107 /**
108 * The caller *may* (but is not required) to chain these ops together. If they are chained
109 * then prepare() and execute() will be called on the head op but not the other ops in the
110 * chain. The head op will prepare and execute on behalf of all the ops in the chain.
111 */
112 kMayChain,
113 /**
114 * The ops cannot be combined.
115 */
116 kCannotCombine
117 };
118
119 // The arenas are the same as what was available when the op was created.
120 CombineResult combineIfPossible(GrOp* that, SkArenaAlloc* alloc, const GrCaps& caps);
121
122 const SkRect& bounds() const {
123 SkASSERT(kUninitialized_BoundsFlag != fBoundsFlags);
124 return fBounds;
125 }
126
127 void setClippedBounds(const SkRect& clippedBounds) {
128 fBounds = clippedBounds;
129 // The clipped bounds already incorporate any effect of the bounds flags.
130 fBoundsFlags = 0;
131 }
132
133 bool hasAABloat() const {
134 SkASSERT(fBoundsFlags != kUninitialized_BoundsFlag);
135 return SkToBool(fBoundsFlags & kAABloat_BoundsFlag);
136 }
137
138 bool hasZeroArea() const {
139 SkASSERT(fBoundsFlags != kUninitialized_BoundsFlag);
140 return SkToBool(fBoundsFlags & kZeroArea_BoundsFlag);
141 }
142
143 void operator delete(void* p) { ::operator delete(p); }
144
145 /**
146 * Helper for safely down-casting to a GrOp subclass
147 */
148 template <typename T> const T& cast() const {
149 SkASSERT(T::ClassID() == this->classID());
150 return *static_cast<const T*>(this);
151 }
152
153 template <typename T> T* cast() {
154 SkASSERT(T::ClassID() == this->classID());
155 return static_cast<T*>(this);
156 }
157
158 uint32_t classID() const { SkASSERT(kIllegalOpID != fClassID); return fClassID; }
159
160 // We lazily initialize the uniqueID because currently the only user is GrAuditTrail
161 uint32_t uniqueID() const {
162 if (kIllegalOpID == fUniqueID) {
163 fUniqueID = GenOpID();
164 }
165 return fUniqueID;
166 }
167
168 /**
169 * This can optionally be called before 'prepare' (but after sorting). Each op that overrides
170 * onPrePrepare must be prepared to handle both cases (when onPrePrepare has been called
171 * ahead of time and when it has not been called).
172 */
173 void prePrepare(GrRecordingContext* context, const GrSurfaceProxyView& dstView,
174 GrAppliedClip* clip, const GrDstProxyView& dstProxyView,
175 GrXferBarrierFlags renderPassXferBarriers, GrLoadOp colorLoadOp) {
177 this->onPrePrepare(context, dstView, clip, dstProxyView, renderPassXferBarriers,
178 colorLoadOp);
179 }
180
181 /**
182 * Called prior to executing. The op should perform any resource creation or data transfers
183 * necessary before execute() is called.
184 */
187 this->onPrepare(state);
188 }
189
190 /** Issues the op's commands to GrGpu. */
191 void execute(GrOpFlushState* state, const SkRect& chainBounds) {
193 this->onExecute(state, chainBounds);
194 }
195
196 /** Used for spewing information about ops when debugging. */
197#if defined(GR_TEST_UTILS)
198 virtual SkString dumpInfo() const final {
199 return SkStringPrintf("%s\nOpBounds: [L: %.2f, T: %.2f, R: %.2f, B: %.2f]",
200 this->onDumpInfo().c_str(), fBounds.fLeft, fBounds.fTop,
201 fBounds.fRight, fBounds.fBottom);
202 }
203#endif
204
205 /**
206 * A helper for iterating over an op chain in a range for loop that also downcasts to a GrOp
207 * subclass. E.g.:
208 * for (MyOpSubClass& op : ChainRange<MyOpSubClass>(this)) {
209 * // ...
210 * }
211 */
212 template <typename OpSubclass = GrOp> class ChainRange {
213 private:
214 class Iter {
215 public:
216 explicit Iter(const OpSubclass* head) : fCurr(head) {}
217 inline Iter& operator++() {
218 return *this = Iter(static_cast<const OpSubclass*>(fCurr->nextInChain()));
219 }
220 const OpSubclass& operator*() const { return *fCurr; }
221 bool operator!=(const Iter& that) const { return fCurr != that.fCurr; }
222
223 private:
224 const OpSubclass* fCurr;
225 };
226 const OpSubclass* fHead;
227
228 public:
229 explicit ChainRange(const OpSubclass* head) : fHead(head) {}
230 Iter begin() { return Iter(fHead); }
231 Iter end() { return Iter(nullptr); }
232 };
233
234 /**
235 * Concatenates two op chains. This op must be a tail and the passed op must be a head. The ops
236 * must be of the same subclass.
237 */
239 /** Returns true if this is the head of a chain (including a length 1 chain). */
240 bool isChainHead() const { return !fPrevInChain; }
241 /** Returns true if this is the tail of a chain (including a length 1 chain). */
242 bool isChainTail() const { return !fNextInChain; }
243 /** The next op in the chain. */
244 GrOp* nextInChain() const { return fNextInChain.get(); }
245 /** The previous op in the chain. */
246 GrOp* prevInChain() const { return fPrevInChain; }
247 /**
248 * Cuts the chain after this op. The returned op is the op that was previously next in the
249 * chain or null if this was already a tail.
250 */
252 SkDEBUGCODE(void validateChain(GrOp* expectedTail = nullptr) const;)
253
254#ifdef SK_DEBUG
255 virtual void validate() const {}
256#endif
257
258protected:
259 GrOp(uint32_t classID);
260
261 /**
262 * Indicates that the op will produce geometry that extends beyond its bounds for the
263 * purpose of ensuring that the fragment shader runs on partially covered pixels for
264 * non-MSAA antialiasing.
265 */
266 enum class HasAABloat : bool {
267 kNo = false,
268 kYes = true
269 };
270 /**
271 * Indicates that the geometry being drawn in a hairline stroke. A point that is drawn in device
272 * space is also considered a hairline.
273 */
274 enum class IsHairline : bool {
275 kNo = false,
276 kYes = true
277 };
278
279 void setBounds(const SkRect& newBounds, HasAABloat aabloat, IsHairline zeroArea) {
280 fBounds = newBounds;
281 this->setBoundsFlags(aabloat, zeroArea);
282 }
283 void setTransformedBounds(const SkRect& srcBounds, const SkMatrix& m,
284 HasAABloat aabloat, IsHairline zeroArea) {
285 m.mapRect(&fBounds, srcBounds);
286 this->setBoundsFlags(aabloat, zeroArea);
287 }
290 }
291
292 static uint32_t GenOpClassID() { return GenID(&gCurrOpClassID); }
293
294private:
295 void joinBounds(const GrOp& that) {
296 if (that.hasAABloat()) {
297 fBoundsFlags |= kAABloat_BoundsFlag;
298 }
299 if (that.hasZeroArea()) {
300 fBoundsFlags |= kZeroArea_BoundsFlag;
301 }
302 return fBounds.joinPossiblyEmptyRect(that.fBounds);
303 }
304
307 }
308
309 // TODO: the parameters to onPrePrepare mirror GrOpFlushState::OpArgs - fuse the two?
311 const GrSurfaceProxyView& writeView,
313 const GrDstProxyView&,
314 GrXferBarrierFlags renderPassXferBarriers,
315 GrLoadOp colorLoadOp) = 0;
316 virtual void onPrepare(GrOpFlushState*) = 0;
317 // If this op is chained then chainBounds is the union of the bounds of all ops in the chain.
318 // Otherwise, this op's bounds.
319 virtual void onExecute(GrOpFlushState*, const SkRect& chainBounds) = 0;
320#if defined(GR_TEST_UTILS)
321 virtual SkString onDumpInfo() const { return SkString(); }
322#endif
323
324 static uint32_t GenID(std::atomic<uint32_t>* idCounter) {
325 uint32_t id = idCounter->fetch_add(1, std::memory_order_relaxed);
326 if (id == 0) {
327 SK_ABORT("This should never wrap as it should only be called once for each GrOp "
328 "subclass.");
329 }
330 return id;
331 }
332
333 void setBoundsFlags(HasAABloat aabloat, IsHairline zeroArea) {
334 fBoundsFlags = 0;
335 fBoundsFlags |= (HasAABloat::kYes == aabloat) ? kAABloat_BoundsFlag : 0;
336 fBoundsFlags |= (IsHairline ::kYes == zeroArea) ? kZeroArea_BoundsFlag : 0;
337 }
338
339 enum {
340 kIllegalOpID = 0,
341 };
342
343 enum BoundsFlags {
344 kAABloat_BoundsFlag = 0x1,
345 kZeroArea_BoundsFlag = 0x2,
346 SkDEBUGCODE(kUninitialized_BoundsFlag = 0x4)
347 };
348
349 Owner fNextInChain{nullptr};
350 GrOp* fPrevInChain = nullptr;
351 const uint16_t fClassID;
352 uint16_t fBoundsFlags;
353
354 static uint32_t GenOpID() { return GenID(&gCurrOpUniqueID); }
355 mutable uint32_t fUniqueID = SK_InvalidUniqueID;
356 SkRect fBounds;
357
358 static std::atomic<uint32_t> gCurrOpUniqueID;
359 static std::atomic<uint32_t> gCurrOpClassID;
360};
361
362#endif
std::function< void(GrSurfaceProxy *, skgpu::Mipmapped)> GrVisitProxyFunc
Definition: GrTypesPriv.h:943
GrLoadOp
Definition: GrTypesPriv.h:155
GrXferBarrierFlags
#define SK_ABORT(message,...)
Definition: SkAssert.h:70
#define SkASSERT(cond)
Definition: SkAssert.h:116
#define SK_DEBUG
@ kYes
Do pre-clip the geometry before applying the (perspective) matrix.
@ kNo
Don't pre-clip the geometry before applying the (perspective) matrix.
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition: SkPath.cpp:3892
SK_API SkString SkStringPrintf(const char *format,...) SK_PRINTF_LIKE(1
Creates a new string and writes into it using a printf()-style format.
static constexpr bool SkToBool(const T &x)
Definition: SkTo.h:35
#define TRACE_EVENT0_ALWAYS(category_group, name)
#define TRACE_STR_STATIC(str)
Definition: SkTraceEvent.h:73
static constexpr uint32_t SK_InvalidUniqueID
Definition: SkTypes.h:196
Definition: GrCaps.h:57
Iter begin()
Definition: GrOp.h:230
Iter end()
Definition: GrOp.h:231
ChainRange(const OpSubclass *head)
Definition: GrOp.h:229
Definition: GrOp.h:70
CombineResult
Definition: GrOp.h:99
CombineResult combineIfPossible(GrOp *that, SkArenaAlloc *alloc, const GrCaps &caps)
Definition: GrOp.cpp:19
GrOp * prevInChain() const
Definition: GrOp.h:246
virtual void onExecute(GrOpFlushState *, const SkRect &chainBounds)=0
virtual ~GrOp()=default
static Owner Make(GrRecordingContext *context, Args &&... args)
Definition: GrOp.h:75
std::unique_ptr< GrOp > Owner
Definition: GrOp.h:72
GrOp * nextInChain() const
Definition: GrOp.h:244
static Owner MakeWithExtraMemory(GrRecordingContext *context, size_t extraSize, Args &&... args)
Definition: GrOp.h:85
void execute(GrOpFlushState *state, const SkRect &chainBounds)
Definition: GrOp.h:191
virtual void onPrepare(GrOpFlushState *)=0
void setClippedBounds(const SkRect &clippedBounds)
Definition: GrOp.h:127
static uint32_t GenOpClassID()
Definition: GrOp.h:292
void prepare(GrOpFlushState *state)
Definition: GrOp.h:185
GrOp::Owner cutChain()
Definition: GrOp.cpp:40
uint32_t uniqueID() const
Definition: GrOp.h:161
bool isChainTail() const
Definition: GrOp.h:242
static Owner MakeWithProcessorSet(GrRecordingContext *context, const SkPMColor4f &color, GrPaint &&paint, Args &&... args)
void prePrepare(GrRecordingContext *context, const GrSurfaceProxyView &dstView, GrAppliedClip *clip, const GrDstProxyView &dstProxyView, GrXferBarrierFlags renderPassXferBarriers, GrLoadOp colorLoadOp)
Definition: GrOp.h:173
HasAABloat
Definition: GrOp.h:266
virtual const char * name() const =0
virtual void onPrePrepare(GrRecordingContext *, const GrSurfaceProxyView &writeView, GrAppliedClip *, const GrDstProxyView &, GrXferBarrierFlags renderPassXferBarriers, GrLoadOp colorLoadOp)=0
T * cast()
Definition: GrOp.h:153
const T & cast() const
Definition: GrOp.h:148
uint32_t classID() const
Definition: GrOp.h:158
const SkRect & bounds() const
Definition: GrOp.h:122
bool hasAABloat() const
Definition: GrOp.h:133
virtual void visitProxies(const GrVisitProxyFunc &) const
Definition: GrOp.h:95
void chainConcat(GrOp::Owner)
Definition: GrOp.cpp:31
IsHairline
Definition: GrOp.h:274
bool isChainHead() const
Definition: GrOp.h:240
bool hasZeroArea() const
Definition: GrOp.h:138
void setBounds(const SkRect &newBounds, HasAABloat aabloat, IsHairline zeroArea)
Definition: GrOp.h:279
void makeFullScreen(GrSurfaceProxy *proxy)
Definition: GrOp.h:288
virtual CombineResult onCombineIfPossible(GrOp *, SkArenaAlloc *, const GrCaps &)
Definition: GrOp.h:305
void setTransformedBounds(const SkRect &srcBounds, const SkMatrix &m, HasAABloat aabloat, IsHairline zeroArea)
Definition: GrOp.h:283
SkRect getBoundsRect() const
const Paint & paint
Definition: color_source.cc:38
DlColor color
AtkStateType state
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
bool operator!=(C p1, const scoped_nsprotocol< C > &p2)
constexpr Color operator*(T value, const Color &c)
Definition: color.h:911
#define T
Definition: precompiler.cc:65
SkScalar fBottom
larger y-axis bounds
Definition: extension.cpp:17
void joinPossiblyEmptyRect(const SkRect &r)
Definition: SkRect.h:1174
SkScalar fLeft
smaller x-axis bounds
Definition: extension.cpp:14
SkScalar fRight
larger x-axis bounds
Definition: extension.cpp:16
SkScalar fTop
smaller y-axis bounds
Definition: extension.cpp:15
const uintptr_t id