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