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