1 /* 2 * Copyright 2016 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 GrAuditTrail_DEFINED 9 #define GrAuditTrail_DEFINED 10 11 #include "GrConfig.h" 12 #include "GrGpuResource.h" 13 #include "GrRenderTargetProxy.h" 14 #include "SkRect.h" 15 #include "SkString.h" 16 #include "SkTArray.h" 17 #include "SkTHash.h" 18 19 class GrOp; 20 21 /* 22 * GrAuditTrail collects a list of draw ops, detailed information about those ops, and can dump them 23 * to json. 24 * 25 * Capturing this information is expensive and consumes a lot of memory, therefore it is important 26 * to enable auditing only when required and disable it promptly. The AutoEnable class helps to 27 * ensure that the audit trail is disabled in a timely fashion. Once the information has been dealt 28 * with, be sure to call reset(), or the log will simply keep growing. 29 */ 30 class GrAuditTrail { 31 public: GrAuditTrail()32 GrAuditTrail() 33 : fClientID(kGrAuditTrailInvalidID) 34 , fEnabled(false) {} 35 36 class AutoEnable { 37 public: AutoEnable(GrAuditTrail * auditTrail)38 AutoEnable(GrAuditTrail* auditTrail) 39 : fAuditTrail(auditTrail) { 40 SkASSERT(!fAuditTrail->isEnabled()); 41 fAuditTrail->setEnabled(true); 42 } 43 ~AutoEnable()44 ~AutoEnable() { 45 SkASSERT(fAuditTrail->isEnabled()); 46 fAuditTrail->setEnabled(false); 47 } 48 49 private: 50 GrAuditTrail* fAuditTrail; 51 }; 52 53 class AutoManageOpList { 54 public: AutoManageOpList(GrAuditTrail * auditTrail)55 AutoManageOpList(GrAuditTrail* auditTrail) 56 : fAutoEnable(auditTrail), fAuditTrail(auditTrail) {} 57 ~AutoManageOpList()58 ~AutoManageOpList() { fAuditTrail->fullReset(); } 59 60 private: 61 AutoEnable fAutoEnable; 62 GrAuditTrail* fAuditTrail; 63 }; 64 65 class AutoCollectOps { 66 public: AutoCollectOps(GrAuditTrail * auditTrail,int clientID)67 AutoCollectOps(GrAuditTrail* auditTrail, int clientID) 68 : fAutoEnable(auditTrail), fAuditTrail(auditTrail) { 69 fAuditTrail->setClientID(clientID); 70 } 71 ~AutoCollectOps()72 ~AutoCollectOps() { fAuditTrail->setClientID(kGrAuditTrailInvalidID); } 73 74 private: 75 AutoEnable fAutoEnable; 76 GrAuditTrail* fAuditTrail; 77 }; 78 pushFrame(const char * framename)79 void pushFrame(const char* framename) { 80 SkASSERT(fEnabled); 81 fCurrentStackTrace.push_back(SkString(framename)); 82 } 83 84 void addOp(const GrOp*, GrRenderTargetProxy::UniqueID proxyID); 85 86 void opsCombined(const GrOp* consumer, const GrOp* consumed); 87 88 // Because op combining is heavily dependent on sequence of draw calls, these calls will only 89 // produce valid information for the given draw sequence which preceeded them. Specifically, ops 90 // of future draw calls may combine with previous ops and thus would invalidate the json. What 91 // this means is that for some sequence of draw calls N, the below toJson calls will only 92 // produce JSON which reflects N draw calls. This JSON may or may not be accurate for N + 1 or 93 // N - 1 draws depending on the actual combining algorithm used. 94 SkString toJson(bool prettyPrint = false) const; 95 96 // returns a json string of all of the ops associated with a given client id 97 SkString toJson(int clientID, bool prettyPrint = false) const; 98 isEnabled()99 bool isEnabled() { return fEnabled; } setEnabled(bool enabled)100 void setEnabled(bool enabled) { fEnabled = enabled; } 101 setClientID(int clientID)102 void setClientID(int clientID) { fClientID = clientID; } 103 104 // We could just return our internal bookkeeping struct if copying the data out becomes 105 // a performance issue, but until then its nice to decouple 106 struct OpInfo { 107 struct Op { 108 int fClientID; 109 SkRect fBounds; 110 }; 111 112 SkRect fBounds; 113 GrSurfaceProxy::UniqueID fProxyUniqueID; 114 SkTArray<Op> fOps; 115 }; 116 117 void getBoundsByClientID(SkTArray<OpInfo>* outInfo, int clientID); 118 void getBoundsByOpListID(OpInfo* outInfo, int opListID); 119 120 void fullReset(); 121 122 static const int kGrAuditTrailInvalidID; 123 124 private: 125 // TODO if performance becomes an issue, we can move to using SkVarAlloc 126 struct Op { 127 SkString toJson() const; 128 SkString fName; 129 SkTArray<SkString> fStackTrace; 130 SkRect fBounds; 131 int fClientID; 132 int fOpListID; 133 int fChildID; 134 }; 135 typedef SkTArray<std::unique_ptr<Op>, true> OpPool; 136 137 typedef SkTArray<Op*> Ops; 138 139 struct OpNode { OpNodeOpNode140 OpNode(const GrSurfaceProxy::UniqueID& proxyID) : fProxyUniqueID(proxyID) { } 141 SkString toJson() const; 142 143 SkRect fBounds; 144 Ops fChildren; 145 const GrSurfaceProxy::UniqueID fProxyUniqueID; 146 }; 147 typedef SkTArray<std::unique_ptr<OpNode>, true> OpList; 148 149 void copyOutFromOpList(OpInfo* outOpInfo, int opListID); 150 151 template <typename T> 152 static void JsonifyTArray(SkString* json, const char* name, const T& array, 153 bool addComma); 154 155 OpPool fOpPool; 156 SkTHashMap<uint32_t, int> fIDLookup; 157 SkTHashMap<int, Ops*> fClientIDLookup; 158 OpList fOpList; 159 SkTArray<SkString> fCurrentStackTrace; 160 161 // The client can pass in an optional client ID which we will use to mark the ops 162 int fClientID; 163 bool fEnabled; 164 }; 165 166 #define GR_AUDIT_TRAIL_INVOKE_GUARD(audit_trail, invoke, ...) \ 167 if (audit_trail->isEnabled()) { \ 168 audit_trail->invoke(__VA_ARGS__); \ 169 } 170 171 #define GR_AUDIT_TRAIL_AUTO_FRAME(audit_trail, framename) \ 172 GR_AUDIT_TRAIL_INVOKE_GUARD((audit_trail), pushFrame, framename); 173 174 #define GR_AUDIT_TRAIL_RESET(audit_trail) \ 175 //GR_AUDIT_TRAIL_INVOKE_GUARD(audit_trail, fullReset); 176 177 #define GR_AUDIT_TRAIL_ADD_OP(audit_trail, op, proxy_id) \ 178 GR_AUDIT_TRAIL_INVOKE_GUARD(audit_trail, addOp, op, proxy_id); 179 180 #define GR_AUDIT_TRAIL_OPS_RESULT_COMBINED(audit_trail, combineWith, op) \ 181 GR_AUDIT_TRAIL_INVOKE_GUARD(audit_trail, opsCombined, combineWith, op); 182 183 #define GR_AUDIT_TRAIL_OP_RESULT_NEW(audit_trail, op) // Doesn't do anything now, one day... 184 185 #endif 186