blob: 2eba92b70a7b8b5143f7fe73c31e79cfa475e607 [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
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "include/core/SkMatrix.h"
12#include "include/core/SkRect.h"
13#include "include/core/SkString.h"
14#include "include/gpu/GrGpuResource.h"
15#include "src/gpu/GrNonAtomicRef.h"
16#include "src/gpu/GrTracing.h"
17#include "src/gpu/GrXferProcessor.h"
Mike Klein0ec1c572018-12-04 11:52:51 -050018#include <atomic>
19#include <new>
joshualitt4d8da812015-01-28 12:53:54 -080020
bsalomon16b99132015-08-13 14:55:50 -070021class GrCaps;
Brian Salomon742e31d2016-12-07 17:06:19 -050022class GrOpFlushState;
Greg Daniel2d41d0d2019-08-26 11:08:51 -040023class GrOpsRenderPass;
joshualitt4d8da812015-01-28 12:53:54 -080024
bsalomonabd30f52015-08-13 13:34:48 -070025/**
Brian Salomon53e4c3c2016-12-21 11:38:53 -050026 * 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.
joshualitt4d8da812015-01-28 12:53:54 -080031 *
Brian Salomond25f5bc2018-08-08 11:25:17 -040032 * Ops of the same subclass may be merged or chained using combineIfPossible. When two ops merge,
33 * one 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. When ops are chained each op maintains its own
35 * data but they are linked in a list and the head op becomes responsible for executing the work for
36 * the chain.
bsalomondb4758c2015-11-23 11:14:20 -080037 *
Brian Salomon588cec72018-11-14 13:56:37 -050038 * It is required that chainability is transitive. Moreover, if op A is able to merge with B then
39 * it must be the case that any op that can chain with A will either merge or chain with any op
40 * that can chain to B.
41 *
Brian Salomon25a88092016-12-01 09:36:50 -050042 * The bounds of the op must contain all the vertices in device space *irrespective* of the clip.
bsalomondb4758c2015-11-23 11:14:20 -080043 * The bounds are used in determining which clip elements must be applied and thus the bounds cannot
44 * in turn depend upon the clip.
joshualitt4d8da812015-01-28 12:53:54 -080045 */
Brian Salomon25a88092016-12-01 09:36:50 -050046#define GR_OP_SPEW 0
47#if GR_OP_SPEW
48 #define GrOP_SPEW(code) code
49 #define GrOP_INFO(...) SkDebugf(__VA_ARGS__)
joshualittca1f07e2015-08-07 08:11:19 -070050#else
Brian Salomon25a88092016-12-01 09:36:50 -050051 #define GrOP_SPEW(code)
52 #define GrOP_INFO(...)
joshualittca1f07e2015-08-07 08:11:19 -070053#endif
joshualitt4d8da812015-01-28 12:53:54 -080054
Robert Phillips27483912018-04-20 12:43:18 -040055// Print out op information at flush time
56#define GR_FLUSH_TIME_OP_SPEW 0
57
reed1b55a962015-09-17 20:16:13 -070058// A helper macro to generate a class static id
Brian Salomon25a88092016-12-01 09:36:50 -050059#define DEFINE_OP_CLASS_ID \
reed1b55a962015-09-17 20:16:13 -070060 static uint32_t ClassID() { \
Brian Salomon25a88092016-12-01 09:36:50 -050061 static uint32_t kClassID = GenOpClassID(); \
reed1b55a962015-09-17 20:16:13 -070062 return kClassID; \
63 }
64
Brian Salomonf8334782017-01-03 09:42:58 -050065class GrOp : private SkNoncopyable {
joshualitt4d8da812015-01-28 12:53:54 -080066public:
Brian Salomon588cec72018-11-14 13:56:37 -050067 virtual ~GrOp() = default;
joshualitt4d8da812015-01-28 12:53:54 -080068
69 virtual const char* name() const = 0;
joshualitt4d8da812015-01-28 12:53:54 -080070
Greg Danieldcf9ca12019-08-27 14:30:21 -040071 using VisitProxyFunc = std::function<void(GrTextureProxy*, GrMipMapped)>;
Robert Phillipsb493eeb2017-09-13 13:10:52 -040072
Chris Dalton1706cbf2019-05-21 19:35:29 -060073 virtual void visitProxies(const VisitProxyFunc&) const {
Robert Phillipsb493eeb2017-09-13 13:10:52 -040074 // This default implementation assumes the op has no proxies
75 }
76
Brian Salomon7eae3e02018-08-07 14:02:38 +000077 enum class CombineResult {
78 /**
79 * The op that combineIfPossible was called on now represents its own work plus that of
Brian Salomond25f5bc2018-08-08 11:25:17 -040080 * the passed op. The passed op should be destroyed without being flushed. Currently it
81 * is not legal to merge an op passed to combineIfPossible() the passed op is already in a
82 * chain (though the op on which combineIfPossible() was called may be).
Brian Salomon7eae3e02018-08-07 14:02:38 +000083 */
84 kMerged,
85 /**
Brian Salomond25f5bc2018-08-08 11:25:17 -040086 * The caller *may* (but is not required) to chain these ops together. If they are chained
87 * then prepare() and execute() will be called on the head op but not the other ops in the
88 * chain. The head op will prepare and execute on behalf of all the ops in the chain.
89 */
90 kMayChain,
91 /**
Brian Salomon7eae3e02018-08-07 14:02:38 +000092 * The ops cannot be combined.
93 */
94 kCannotCombine
95 };
96
Brian Salomond25f5bc2018-08-08 11:25:17 -040097 CombineResult combineIfPossible(GrOp* that, const GrCaps& caps);
joshualitt4d8da812015-01-28 12:53:54 -080098
bsalomon88cf17d2016-07-08 06:40:56 -070099 const SkRect& bounds() const {
100 SkASSERT(kUninitialized_BoundsFlag != fBoundsFlags);
101 return fBounds;
102 }
103
Brian Salomon9e50f7b2017-03-06 12:02:34 -0500104 void setClippedBounds(const SkRect& clippedBounds) {
105 fBounds = clippedBounds;
106 // The clipped bounds already incorporate any effect of the bounds flags.
107 fBoundsFlags = 0;
108 }
109
bsalomon88cf17d2016-07-08 06:40:56 -0700110 bool hasAABloat() const {
111 SkASSERT(fBoundsFlags != kUninitialized_BoundsFlag);
112 return SkToBool(fBoundsFlags & kAABloat_BoundsFlag);
113 }
114
115 bool hasZeroArea() const {
116 SkASSERT(fBoundsFlags != kUninitialized_BoundsFlag);
117 return SkToBool(fBoundsFlags & kZeroArea_BoundsFlag);
118 }
joshualitt99c7c072015-05-01 13:43:30 -0700119
Robert Phillipsc994a932018-06-19 13:09:54 -0400120#ifdef SK_DEBUG
121 // All GrOp-derived classes should be allocated in and deleted from a GrMemoryPool
joshualitt4d8da812015-01-28 12:53:54 -0800122 void* operator new(size_t size);
123 void operator delete(void* target);
124
125 void* operator new(size_t size, void* placement) {
126 return ::operator new(size, placement);
127 }
128 void operator delete(void* target, void* placement) {
129 ::operator delete(target, placement);
130 }
Robert Phillipsc994a932018-06-19 13:09:54 -0400131#endif
joshualitt4d8da812015-01-28 12:53:54 -0800132
133 /**
Brian Salomon25a88092016-12-01 09:36:50 -0500134 * Helper for safely down-casting to a GrOp subclass
bsalomonabd30f52015-08-13 13:34:48 -0700135 */
reed1b55a962015-09-17 20:16:13 -0700136 template <typename T> const T& cast() const {
137 SkASSERT(T::ClassID() == this->classID());
138 return *static_cast<const T*>(this);
139 }
140
141 template <typename T> T* cast() {
142 SkASSERT(T::ClassID() == this->classID());
143 return static_cast<T*>(this);
144 }
joshualitt4d8da812015-01-28 12:53:54 -0800145
Brian Salomon25a88092016-12-01 09:36:50 -0500146 uint32_t classID() const { SkASSERT(kIllegalOpID != fClassID); return fClassID; }
joshualitt4d8da812015-01-28 12:53:54 -0800147
joshualitt08e65e72016-03-08 09:31:15 -0800148 // We lazily initialize the uniqueID because currently the only user is GrAuditTrail
halcanary9d524f22016-03-29 09:03:52 -0700149 uint32_t uniqueID() const {
Brian Salomon25a88092016-12-01 09:36:50 -0500150 if (kIllegalOpID == fUniqueID) {
151 fUniqueID = GenOpID();
joshualitt08e65e72016-03-08 09:31:15 -0800152 }
halcanary9d524f22016-03-29 09:03:52 -0700153 return fUniqueID;
joshualitt08e65e72016-03-08 09:31:15 -0800154 }
joshualittca1f07e2015-08-07 08:11:19 -0700155
Brian Salomonbde42852016-12-21 11:37:49 -0500156 /**
157 * Called prior to executing. The op should perform any resource creation or data transfers
158 * necessary before execute() is called.
159 */
Brian Salomon742e31d2016-12-07 17:06:19 -0500160 void prepare(GrOpFlushState* state) { this->onPrepare(state); }
bsalomon53469832015-08-18 09:20:09 -0700161
Brian Salomon25a88092016-12-01 09:36:50 -0500162 /** Issues the op's commands to GrGpu. */
Brian Salomon588cec72018-11-14 13:56:37 -0500163 void execute(GrOpFlushState* state, const SkRect& chainBounds) {
Brian Salomon5f394272019-07-02 14:07:49 -0400164 TRACE_EVENT0("skia.gpu", name());
Brian Salomon588cec72018-11-14 13:56:37 -0500165 this->onExecute(state, chainBounds);
Xiao Yu97126012018-04-25 18:11:44 -0700166 }
bsalomon53469832015-08-18 09:20:09 -0700167
Brian Salomon25a88092016-12-01 09:36:50 -0500168 /** Used for spewing information about ops when debugging. */
Brian Osman9a390ac2018-11-12 09:47:48 -0500169#ifdef SK_DEBUG
robertphillips44fbc792016-06-29 06:56:12 -0700170 virtual SkString dumpInfo() const {
171 SkString string;
Brian Salomon25a88092016-12-01 09:36:50 -0500172 string.appendf("OpBounds: [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n",
robertphillips44fbc792016-06-29 06:56:12 -0700173 fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
174 return string;
175 }
Brian Osman9a390ac2018-11-12 09:47:48 -0500176#else
177 SkString dumpInfo() const { return SkString("<Op information unavailable>"); }
178#endif
bsalomon53469832015-08-18 09:20:09 -0700179
Brian Salomond25f5bc2018-08-08 11:25:17 -0400180 /**
181 * A helper for iterating over an op chain in a range for loop that also downcasts to a GrOp
182 * subclass. E.g.:
183 * for (MyOpSubClass& op : ChainRange<MyOpSubClass>(this)) {
184 * // ...
185 * }
186 */
Brian Salomon588cec72018-11-14 13:56:37 -0500187 template <typename OpSubclass = GrOp> class ChainRange {
Brian Salomond25f5bc2018-08-08 11:25:17 -0400188 private:
189 class Iter {
190 public:
Brian Salomon588cec72018-11-14 13:56:37 -0500191 explicit Iter(const OpSubclass* head) : fCurr(head) {}
192 inline Iter& operator++() {
193 return *this = Iter(static_cast<const OpSubclass*>(fCurr->nextInChain()));
194 }
195 const OpSubclass& operator*() const { return *fCurr; }
Brian Salomond25f5bc2018-08-08 11:25:17 -0400196 bool operator!=(const Iter& that) const { return fCurr != that.fCurr; }
197
198 private:
Brian Salomon588cec72018-11-14 13:56:37 -0500199 const OpSubclass* fCurr;
Brian Salomond25f5bc2018-08-08 11:25:17 -0400200 };
Brian Salomon588cec72018-11-14 13:56:37 -0500201 const OpSubclass* fHead;
Brian Salomond25f5bc2018-08-08 11:25:17 -0400202
203 public:
Brian Salomon588cec72018-11-14 13:56:37 -0500204 explicit ChainRange(const OpSubclass* head) : fHead(head) {}
Brian Salomond25f5bc2018-08-08 11:25:17 -0400205 Iter begin() { return Iter(fHead); }
206 Iter end() { return Iter(nullptr); }
207 };
208
Brian Salomon588cec72018-11-14 13:56:37 -0500209 /**
210 * Concatenates two op chains. This op must be a tail and the passed op must be a head. The ops
211 * must be of the same subclass.
212 */
213 void chainConcat(std::unique_ptr<GrOp>);
Brian Salomond25f5bc2018-08-08 11:25:17 -0400214 /** Returns true if this is the head of a chain (including a length 1 chain). */
Brian Salomon588cec72018-11-14 13:56:37 -0500215 bool isChainHead() const { return !fPrevInChain; }
Brian Salomond25f5bc2018-08-08 11:25:17 -0400216 /** Returns true if this is the tail of a chain (including a length 1 chain). */
217 bool isChainTail() const { return !fNextInChain; }
Brian Salomond25f5bc2018-08-08 11:25:17 -0400218 /** The next op in the chain. */
Brian Salomon588cec72018-11-14 13:56:37 -0500219 GrOp* nextInChain() const { return fNextInChain.get(); }
220 /** The previous op in the chain. */
221 GrOp* prevInChain() const { return fPrevInChain; }
222 /**
223 * Cuts the chain after this op. The returned op is the op that was previously next in the
224 * chain or null if this was already a tail.
225 */
226 std::unique_ptr<GrOp> cutChain();
227 SkDEBUGCODE(void validateChain(GrOp* expectedTail = nullptr) const);
Brian Salomond25f5bc2018-08-08 11:25:17 -0400228
Ethan Nicholas029b22c2018-10-18 16:49:56 -0400229#ifdef SK_DEBUG
230 virtual void validate() const {}
231#endif
232
joshualitt4d8da812015-01-28 12:53:54 -0800233protected:
Brian Salomond25f5bc2018-08-08 11:25:17 -0400234 GrOp(uint32_t classID);
235
bsalomon88cf17d2016-07-08 06:40:56 -0700236 /**
Brian Salomon25a88092016-12-01 09:36:50 -0500237 * Indicates that the op will produce geometry that extends beyond its bounds for the
bsalomon88cf17d2016-07-08 06:40:56 -0700238 * purpose of ensuring that the fragment shader runs on partially covered pixels for
239 * non-MSAA antialiasing.
240 */
Chris Dalton3b51df12017-11-27 14:33:06 -0700241 enum class HasAABloat : bool {
242 kNo = false,
243 kYes = true
bsalomon88cf17d2016-07-08 06:40:56 -0700244 };
245 /**
Brian Salomon25a88092016-12-01 09:36:50 -0500246 * Indicates that the geometry represented by the op has zero area (e.g. it is hairline or
247 * points).
bsalomon88cf17d2016-07-08 06:40:56 -0700248 */
Chris Dalton3b51df12017-11-27 14:33:06 -0700249 enum class IsZeroArea : bool {
250 kNo = false,
251 kYes = true
bsalomon88cf17d2016-07-08 06:40:56 -0700252 };
Robert Phillips65a88fa2017-08-08 08:36:22 -0400253
bsalomon88cf17d2016-07-08 06:40:56 -0700254 void setBounds(const SkRect& newBounds, HasAABloat aabloat, IsZeroArea zeroArea) {
255 fBounds = newBounds;
256 this->setBoundsFlags(aabloat, zeroArea);
257 }
258 void setTransformedBounds(const SkRect& srcBounds, const SkMatrix& m,
259 HasAABloat aabloat, IsZeroArea zeroArea) {
260 m.mapRect(&fBounds, srcBounds);
261 this->setBoundsFlags(aabloat, zeroArea);
262 }
Robert Phillips65a88fa2017-08-08 08:36:22 -0400263 void makeFullScreen(GrSurfaceProxy* proxy) {
264 this->setBounds(SkRect::MakeIWH(proxy->width(), proxy->height()),
265 HasAABloat::kNo, IsZeroArea::kNo);
266 }
joshualitt99c7c072015-05-01 13:43:30 -0700267
Brian Salomonb41417f2018-10-24 08:58:48 -0400268 static uint32_t GenOpClassID() { return GenID(&gCurrOpClassID); }
269
270private:
Brian Salomon25a88092016-12-01 09:36:50 -0500271 void joinBounds(const GrOp& that) {
bsalomon88cf17d2016-07-08 06:40:56 -0700272 if (that.hasAABloat()) {
273 fBoundsFlags |= kAABloat_BoundsFlag;
274 }
275 if (that.hasZeroArea()) {
276 fBoundsFlags |= kZeroArea_BoundsFlag;
277 }
278 return fBounds.joinPossiblyEmptyRect(that.fBounds);
279 }
280
Brian Salomon7eae3e02018-08-07 14:02:38 +0000281 virtual CombineResult onCombineIfPossible(GrOp*, const GrCaps&) {
282 return CombineResult::kCannotCombine;
283 }
bsalomonabd30f52015-08-13 13:34:48 -0700284
Brian Salomon742e31d2016-12-07 17:06:19 -0500285 virtual void onPrepare(GrOpFlushState*) = 0;
Brian Salomon588cec72018-11-14 13:56:37 -0500286 // If this op is chained then chainBounds is the union of the bounds of all ops in the chain.
287 // Otherwise, this op's bounds.
288 virtual void onExecute(GrOpFlushState*, const SkRect& chainBounds) = 0;
bsalomon53469832015-08-18 09:20:09 -0700289
Mike Klein0ec1c572018-12-04 11:52:51 -0500290 static uint32_t GenID(std::atomic<uint32_t>* idCounter) {
291 uint32_t id = (*idCounter)++;
292 if (id == 0) {
Ben Wagnerb4aab9a2017-08-16 10:53:04 -0400293 SK_ABORT("This should never wrap as it should only be called once for each GrOp "
bsalomonabd30f52015-08-13 13:34:48 -0700294 "subclass.");
295 }
296 return id;
297 }
298
bsalomon88cf17d2016-07-08 06:40:56 -0700299 void setBoundsFlags(HasAABloat aabloat, IsZeroArea zeroArea) {
300 fBoundsFlags = 0;
301 fBoundsFlags |= (HasAABloat::kYes == aabloat) ? kAABloat_BoundsFlag : 0;
302 fBoundsFlags |= (IsZeroArea ::kYes == zeroArea) ? kZeroArea_BoundsFlag : 0;
303 }
304
bsalomonabd30f52015-08-13 13:34:48 -0700305 enum {
Brian Salomon25a88092016-12-01 09:36:50 -0500306 kIllegalOpID = 0,
bsalomonabd30f52015-08-13 13:34:48 -0700307 };
308
bsalomon88cf17d2016-07-08 06:40:56 -0700309 enum BoundsFlags {
310 kAABloat_BoundsFlag = 0x1,
311 kZeroArea_BoundsFlag = 0x2,
312 SkDEBUGCODE(kUninitialized_BoundsFlag = 0x4)
313 };
314
Brian Salomon588cec72018-11-14 13:56:37 -0500315 std::unique_ptr<GrOp> fNextInChain;
316 GrOp* fPrevInChain = nullptr;
bsalomon88cf17d2016-07-08 06:40:56 -0700317 const uint16_t fClassID;
318 uint16_t fBoundsFlags;
319
Brian Salomon25a88092016-12-01 09:36:50 -0500320 static uint32_t GenOpID() { return GenID(&gCurrOpUniqueID); }
Brian Salomond25f5bc2018-08-08 11:25:17 -0400321 mutable uint32_t fUniqueID = SK_InvalidUniqueID;
bsalomon88cf17d2016-07-08 06:40:56 -0700322 SkRect fBounds;
323
Mike Klein0ec1c572018-12-04 11:52:51 -0500324 static std::atomic<uint32_t> gCurrOpUniqueID;
325 static std::atomic<uint32_t> gCurrOpClassID;
bsalomonabd30f52015-08-13 13:34:48 -0700326};
327
joshualitt4d8da812015-01-28 12:53:54 -0800328#endif