blob: d23f781f1504537fd58d831f42fe2bc2e360dc86 [file] [log] [blame]
joshualitt4d8da812015-01-28 12:53:54 -08001/*
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
Brian Salomon53e4c3c2016-12-21 11:38:53 -05008#ifndef GrOp_DEFINED
9#define GrOp_DEFINED
joshualitt4d8da812015-01-28 12:53:54 -080010
bungeman2c4bd072016-04-08 06:58:51 -070011#include "../private/SkAtomics.h"
Robert Phillips294870f2016-11-11 12:38:40 -050012#include "GrGpuResource.h"
joshualittdbe1e6f2015-07-16 08:12:45 -070013#include "GrNonAtomicRef.h"
Xiao Yu97126012018-04-25 18:11:44 -070014#include "GrTracing.h"
Brian Salomon54d212e2017-03-21 14:22:38 -040015#include "GrXferProcessor.h"
bsalomon88cf17d2016-07-08 06:40:56 -070016#include "SkMatrix.h"
bsalomon16b99132015-08-13 14:55:50 -070017#include "SkRect.h"
bsalomon53469832015-08-18 09:20:09 -070018#include "SkString.h"
joshualitt4d8da812015-01-28 12:53:54 -080019
bungeman2c4bd072016-04-08 06:58:51 -070020#include <new>
21
bsalomon16b99132015-08-13 14:55:50 -070022class GrCaps;
egdaniel9cb63402016-06-23 08:37:05 -070023class GrGpuCommandBuffer;
Brian Salomon742e31d2016-12-07 17:06:19 -050024class GrOpFlushState;
Robert Phillipse3302df2017-04-24 07:31:02 -040025class GrRenderTargetOpList;
joshualitt4d8da812015-01-28 12:53:54 -080026
bsalomonabd30f52015-08-13 13:34:48 -070027/**
Brian Salomon53e4c3c2016-12-21 11:38:53 -050028 * GrOp is the base class for all Ganesh deferred GPU operations. To facilitate reordering and to
29 * minimize draw calls, Ganesh does not generate geometry inline with draw calls. Instead, it
30 * captures the arguments to the draw and then generates the geometry when flushing. This gives GrOp
31 * subclasses complete freedom to decide how/when to combine in order to produce fewer draw calls
32 * and minimize state changes.
joshualitt4d8da812015-01-28 12:53:54 -080033 *
Brian Salomond25f5bc2018-08-08 11:25:17 -040034 * Ops of the same subclass may be merged or chained using combineIfPossible. When two ops merge,
35 * one takes on the union of the data and the other is left empty. The merged op becomes responsible
36 * for drawing the data from both the original ops. When ops are chained each op maintains its own
37 * data but they are linked in a list and the head op becomes responsible for executing the work for
38 * the chain.
bsalomondb4758c2015-11-23 11:14:20 -080039 *
Brian Salomon25a88092016-12-01 09:36:50 -050040 * The bounds of the op must contain all the vertices in device space *irrespective* of the clip.
bsalomondb4758c2015-11-23 11:14:20 -080041 * The bounds are used in determining which clip elements must be applied and thus the bounds cannot
42 * in turn depend upon the clip.
joshualitt4d8da812015-01-28 12:53:54 -080043 */
Brian Salomon25a88092016-12-01 09:36:50 -050044#define GR_OP_SPEW 0
45#if GR_OP_SPEW
46 #define GrOP_SPEW(code) code
47 #define GrOP_INFO(...) SkDebugf(__VA_ARGS__)
joshualittca1f07e2015-08-07 08:11:19 -070048#else
Brian Salomon25a88092016-12-01 09:36:50 -050049 #define GrOP_SPEW(code)
50 #define GrOP_INFO(...)
joshualittca1f07e2015-08-07 08:11:19 -070051#endif
joshualitt4d8da812015-01-28 12:53:54 -080052
Robert Phillips27483912018-04-20 12:43:18 -040053// Print out op information at flush time
54#define GR_FLUSH_TIME_OP_SPEW 0
55
reed1b55a962015-09-17 20:16:13 -070056// A helper macro to generate a class static id
Brian Salomon25a88092016-12-01 09:36:50 -050057#define DEFINE_OP_CLASS_ID \
reed1b55a962015-09-17 20:16:13 -070058 static uint32_t ClassID() { \
Brian Salomon25a88092016-12-01 09:36:50 -050059 static uint32_t kClassID = GenOpClassID(); \
reed1b55a962015-09-17 20:16:13 -070060 return kClassID; \
61 }
62
Brian Salomonf8334782017-01-03 09:42:58 -050063class GrOp : private SkNoncopyable {
joshualitt4d8da812015-01-28 12:53:54 -080064public:
Brian Salomon25a88092016-12-01 09:36:50 -050065 virtual ~GrOp();
joshualitt4d8da812015-01-28 12:53:54 -080066
67 virtual const char* name() const = 0;
joshualitt4d8da812015-01-28 12:53:54 -080068
Robert Phillipsb493eeb2017-09-13 13:10:52 -040069 typedef std::function<void(GrSurfaceProxy*)> VisitProxyFunc;
70
Robert Phillipsf1748f52017-09-14 14:11:24 -040071 virtual void visitProxies(const VisitProxyFunc&) const {
Robert Phillipsb493eeb2017-09-13 13:10:52 -040072 // This default implementation assumes the op has no proxies
73 }
74
Brian Salomon7eae3e02018-08-07 14:02:38 +000075 enum class CombineResult {
76 /**
77 * The op that combineIfPossible was called on now represents its own work plus that of
Brian Salomond25f5bc2018-08-08 11:25:17 -040078 * the passed op. The passed op should be destroyed without being flushed. Currently it
79 * is not legal to merge an op passed to combineIfPossible() the passed op is already in a
80 * chain (though the op on which combineIfPossible() was called may be).
Brian Salomon7eae3e02018-08-07 14:02:38 +000081 */
82 kMerged,
83 /**
Brian Salomond25f5bc2018-08-08 11:25:17 -040084 * The caller *may* (but is not required) to chain these ops together. If they are chained
85 * then prepare() and execute() will be called on the head op but not the other ops in the
86 * chain. The head op will prepare and execute on behalf of all the ops in the chain.
87 */
88 kMayChain,
89 /**
Brian Salomon7eae3e02018-08-07 14:02:38 +000090 * The ops cannot be combined.
91 */
92 kCannotCombine
93 };
94
Brian Salomond25f5bc2018-08-08 11:25:17 -040095 CombineResult combineIfPossible(GrOp* that, const GrCaps& caps);
joshualitt4d8da812015-01-28 12:53:54 -080096
bsalomon88cf17d2016-07-08 06:40:56 -070097 const SkRect& bounds() const {
98 SkASSERT(kUninitialized_BoundsFlag != fBoundsFlags);
99 return fBounds;
100 }
101
Brian Salomon9e50f7b2017-03-06 12:02:34 -0500102 void setClippedBounds(const SkRect& clippedBounds) {
103 fBounds = clippedBounds;
104 // The clipped bounds already incorporate any effect of the bounds flags.
105 fBoundsFlags = 0;
106 }
107
bsalomon88cf17d2016-07-08 06:40:56 -0700108 bool hasAABloat() const {
109 SkASSERT(fBoundsFlags != kUninitialized_BoundsFlag);
110 return SkToBool(fBoundsFlags & kAABloat_BoundsFlag);
111 }
112
113 bool hasZeroArea() const {
114 SkASSERT(fBoundsFlags != kUninitialized_BoundsFlag);
115 return SkToBool(fBoundsFlags & kZeroArea_BoundsFlag);
116 }
joshualitt99c7c072015-05-01 13:43:30 -0700117
Robert Phillipsc994a932018-06-19 13:09:54 -0400118#ifdef SK_DEBUG
119 // All GrOp-derived classes should be allocated in and deleted from a GrMemoryPool
joshualitt4d8da812015-01-28 12:53:54 -0800120 void* operator new(size_t size);
121 void operator delete(void* target);
122
123 void* operator new(size_t size, void* placement) {
124 return ::operator new(size, placement);
125 }
126 void operator delete(void* target, void* placement) {
127 ::operator delete(target, placement);
128 }
Robert Phillipsc994a932018-06-19 13:09:54 -0400129#endif
joshualitt4d8da812015-01-28 12:53:54 -0800130
131 /**
Brian Salomon25a88092016-12-01 09:36:50 -0500132 * Helper for safely down-casting to a GrOp subclass
bsalomonabd30f52015-08-13 13:34:48 -0700133 */
reed1b55a962015-09-17 20:16:13 -0700134 template <typename T> const T& cast() const {
135 SkASSERT(T::ClassID() == this->classID());
136 return *static_cast<const T*>(this);
137 }
138
139 template <typename T> T* cast() {
140 SkASSERT(T::ClassID() == this->classID());
141 return static_cast<T*>(this);
142 }
joshualitt4d8da812015-01-28 12:53:54 -0800143
Brian Salomon25a88092016-12-01 09:36:50 -0500144 uint32_t classID() const { SkASSERT(kIllegalOpID != fClassID); return fClassID; }
joshualitt4d8da812015-01-28 12:53:54 -0800145
joshualitt08e65e72016-03-08 09:31:15 -0800146 // We lazily initialize the uniqueID because currently the only user is GrAuditTrail
halcanary9d524f22016-03-29 09:03:52 -0700147 uint32_t uniqueID() const {
Brian Salomon25a88092016-12-01 09:36:50 -0500148 if (kIllegalOpID == fUniqueID) {
149 fUniqueID = GenOpID();
joshualitt08e65e72016-03-08 09:31:15 -0800150 }
halcanary9d524f22016-03-29 09:03:52 -0700151 return fUniqueID;
joshualitt08e65e72016-03-08 09:31:15 -0800152 }
joshualittca1f07e2015-08-07 08:11:19 -0700153
Brian Salomonbde42852016-12-21 11:37:49 -0500154 /**
155 * Called prior to executing. The op should perform any resource creation or data transfers
156 * necessary before execute() is called.
157 */
Brian Salomon742e31d2016-12-07 17:06:19 -0500158 void prepare(GrOpFlushState* state) { this->onPrepare(state); }
bsalomon53469832015-08-18 09:20:09 -0700159
Brian Salomon25a88092016-12-01 09:36:50 -0500160 /** Issues the op's commands to GrGpu. */
Xiao Yu97126012018-04-25 18:11:44 -0700161 void execute(GrOpFlushState* state) {
162 TRACE_EVENT0("skia", name());
163 this->onExecute(state);
164 }
bsalomon53469832015-08-18 09:20:09 -0700165
Brian Salomon25a88092016-12-01 09:36:50 -0500166 /** Used for spewing information about ops when debugging. */
robertphillips44fbc792016-06-29 06:56:12 -0700167 virtual SkString dumpInfo() const {
168 SkString string;
Brian Salomon25a88092016-12-01 09:36:50 -0500169 string.appendf("OpBounds: [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n",
robertphillips44fbc792016-06-29 06:56:12 -0700170 fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
171 return string;
172 }
bsalomon53469832015-08-18 09:20:09 -0700173
Brian Salomond25f5bc2018-08-08 11:25:17 -0400174 /**
175 * A helper for iterating over an op chain in a range for loop that also downcasts to a GrOp
176 * subclass. E.g.:
177 * for (MyOpSubClass& op : ChainRange<MyOpSubClass>(this)) {
178 * // ...
179 * }
180 */
181 template <typename OpSubclass> class ChainRange {
182 private:
183 class Iter {
184 public:
185 explicit Iter(const GrOp* head) : fCurr(head) {}
186 inline Iter& operator++() { return *this = Iter(fCurr->nextInChain()); }
Brian Salomon764e5462018-08-21 12:07:00 -0400187 const OpSubclass& operator*() const { return fCurr->cast<OpSubclass>(); }
Brian Salomond25f5bc2018-08-08 11:25:17 -0400188 bool operator!=(const Iter& that) const { return fCurr != that.fCurr; }
189
190 private:
191 const GrOp* fCurr;
192 };
193 const GrOp* fHead;
194
195 public:
196 explicit ChainRange(const GrOp* head) : fHead(head) {}
197 Iter begin() { return Iter(fHead); }
198 Iter end() { return Iter(nullptr); }
199 };
200
201 void setNextInChain(GrOp*);
202 /** Returns true if this is the head of a chain (including a length 1 chain). */
203 bool isChainHead() const { return !fChainHead || (fChainHead == this); }
Brian Salomon764e5462018-08-21 12:07:00 -0400204 /** Gets the head op of the chain. */
205 const GrOp* chainHead() const { return fChainHead ? fChainHead : this; }
Brian Salomond25f5bc2018-08-08 11:25:17 -0400206 /** Returns true if this is the tail of a chain (including a length 1 chain). */
207 bool isChainTail() const { return !fNextInChain; }
208 /** Returns true if this is part of chain with length > 1. */
209 bool isChained() const { return SkToBool(fChainHead); }
210 /** The next op in the chain. */
211 const GrOp* nextInChain() const { return fNextInChain; }
212
joshualitt4d8da812015-01-28 12:53:54 -0800213protected:
Brian Salomond25f5bc2018-08-08 11:25:17 -0400214 GrOp(uint32_t classID);
215
bsalomon88cf17d2016-07-08 06:40:56 -0700216 /**
Brian Salomon25a88092016-12-01 09:36:50 -0500217 * Indicates that the op will produce geometry that extends beyond its bounds for the
bsalomon88cf17d2016-07-08 06:40:56 -0700218 * purpose of ensuring that the fragment shader runs on partially covered pixels for
219 * non-MSAA antialiasing.
220 */
Chris Dalton3b51df12017-11-27 14:33:06 -0700221 enum class HasAABloat : bool {
222 kNo = false,
223 kYes = true
bsalomon88cf17d2016-07-08 06:40:56 -0700224 };
225 /**
Brian Salomon25a88092016-12-01 09:36:50 -0500226 * Indicates that the geometry represented by the op has zero area (e.g. it is hairline or
227 * points).
bsalomon88cf17d2016-07-08 06:40:56 -0700228 */
Chris Dalton3b51df12017-11-27 14:33:06 -0700229 enum class IsZeroArea : bool {
230 kNo = false,
231 kYes = true
bsalomon88cf17d2016-07-08 06:40:56 -0700232 };
Robert Phillips65a88fa2017-08-08 08:36:22 -0400233
bsalomon88cf17d2016-07-08 06:40:56 -0700234 void setBounds(const SkRect& newBounds, HasAABloat aabloat, IsZeroArea zeroArea) {
235 fBounds = newBounds;
236 this->setBoundsFlags(aabloat, zeroArea);
237 }
238 void setTransformedBounds(const SkRect& srcBounds, const SkMatrix& m,
239 HasAABloat aabloat, IsZeroArea zeroArea) {
240 m.mapRect(&fBounds, srcBounds);
241 this->setBoundsFlags(aabloat, zeroArea);
242 }
Robert Phillips65a88fa2017-08-08 08:36:22 -0400243 void makeFullScreen(GrSurfaceProxy* proxy) {
244 this->setBounds(SkRect::MakeIWH(proxy->width(), proxy->height()),
245 HasAABloat::kNo, IsZeroArea::kNo);
246 }
joshualitt99c7c072015-05-01 13:43:30 -0700247
Brian Salomon25a88092016-12-01 09:36:50 -0500248 void joinBounds(const GrOp& that) {
bsalomon88cf17d2016-07-08 06:40:56 -0700249 if (that.hasAABloat()) {
250 fBoundsFlags |= kAABloat_BoundsFlag;
251 }
252 if (that.hasZeroArea()) {
253 fBoundsFlags |= kZeroArea_BoundsFlag;
254 }
255 return fBounds.joinPossiblyEmptyRect(that.fBounds);
256 }
257
Brian Salomon25a88092016-12-01 09:36:50 -0500258 void replaceBounds(const GrOp& that) {
bsalomon88cf17d2016-07-08 06:40:56 -0700259 fBounds = that.fBounds;
260 fBoundsFlags = that.fBoundsFlags;
joshualitt99c7c072015-05-01 13:43:30 -0700261 }
262
Brian Salomon25a88092016-12-01 09:36:50 -0500263 static uint32_t GenOpClassID() { return GenID(&gCurrOpClassID); }
reed1b55a962015-09-17 20:16:13 -0700264
bsalomonabd30f52015-08-13 13:34:48 -0700265private:
Brian Salomon7eae3e02018-08-07 14:02:38 +0000266 virtual CombineResult onCombineIfPossible(GrOp*, const GrCaps&) {
267 return CombineResult::kCannotCombine;
268 }
bsalomonabd30f52015-08-13 13:34:48 -0700269
Brian Salomon742e31d2016-12-07 17:06:19 -0500270 virtual void onPrepare(GrOpFlushState*) = 0;
Brian Salomon9e50f7b2017-03-06 12:02:34 -0500271 virtual void onExecute(GrOpFlushState*) = 0;
bsalomon53469832015-08-18 09:20:09 -0700272
bsalomonabd30f52015-08-13 13:34:48 -0700273 static uint32_t GenID(int32_t* idCounter) {
reed1b55a962015-09-17 20:16:13 -0700274 // The atomic inc returns the old value not the incremented value. So we add
bsalomonabd30f52015-08-13 13:34:48 -0700275 // 1 to the returned value.
276 uint32_t id = static_cast<uint32_t>(sk_atomic_inc(idCounter)) + 1;
277 if (!id) {
Ben Wagnerb4aab9a2017-08-16 10:53:04 -0400278 SK_ABORT("This should never wrap as it should only be called once for each GrOp "
bsalomonabd30f52015-08-13 13:34:48 -0700279 "subclass.");
280 }
281 return id;
282 }
283
bsalomon88cf17d2016-07-08 06:40:56 -0700284 void setBoundsFlags(HasAABloat aabloat, IsZeroArea zeroArea) {
285 fBoundsFlags = 0;
286 fBoundsFlags |= (HasAABloat::kYes == aabloat) ? kAABloat_BoundsFlag : 0;
287 fBoundsFlags |= (IsZeroArea ::kYes == zeroArea) ? kZeroArea_BoundsFlag : 0;
288 }
289
bsalomonabd30f52015-08-13 13:34:48 -0700290 enum {
Brian Salomon25a88092016-12-01 09:36:50 -0500291 kIllegalOpID = 0,
bsalomonabd30f52015-08-13 13:34:48 -0700292 };
293
bsalomon88cf17d2016-07-08 06:40:56 -0700294 enum BoundsFlags {
295 kAABloat_BoundsFlag = 0x1,
296 kZeroArea_BoundsFlag = 0x2,
297 SkDEBUGCODE(kUninitialized_BoundsFlag = 0x4)
298 };
299
Brian Salomond25f5bc2018-08-08 11:25:17 -0400300 GrOp* fNextInChain = nullptr;
301 GrOp* fChainHead = nullptr; // null if this is not in a chain.
bsalomon88cf17d2016-07-08 06:40:56 -0700302 const uint16_t fClassID;
303 uint16_t fBoundsFlags;
304
Brian Salomon25a88092016-12-01 09:36:50 -0500305 static uint32_t GenOpID() { return GenID(&gCurrOpUniqueID); }
Brian Salomond25f5bc2018-08-08 11:25:17 -0400306 mutable uint32_t fUniqueID = SK_InvalidUniqueID;
bsalomon88cf17d2016-07-08 06:40:56 -0700307 SkRect fBounds;
308
Brian Salomon25a88092016-12-01 09:36:50 -0500309 static int32_t gCurrOpUniqueID;
310 static int32_t gCurrOpClassID;
bsalomonabd30f52015-08-13 13:34:48 -0700311};
312
joshualitt4d8da812015-01-28 12:53:54 -0800313#endif