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
19class GrOp;
20class 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 */
31class GrAuditTrail {
32public:
33 GrAuditTrail()
34 : fClientID(kGrAuditTrailInvalidID)
35 , fEnabled(false) {}
36
37 class AutoEnable {
38 public:
39 AutoEnable(GrAuditTrail* auditTrail)
40 : fAuditTrail(auditTrail) {
41 SkASSERT(!fAuditTrail->isEnabled());
42 fAuditTrail->setEnabled(true);
43 }
44
45 ~AutoEnable() {
46 SkASSERT(fAuditTrail->isEnabled());
47 fAuditTrail->setEnabled(false);
48 }
49
50 private:
51 GrAuditTrail* fAuditTrail;
52 };
53
54 class AutoManageOpsTask {
55 public:
56 AutoManageOpsTask(GrAuditTrail* auditTrail)
57 : fAutoEnable(auditTrail), fAuditTrail(auditTrail) {}
58
59 ~AutoManageOpsTask() { fAuditTrail->fullReset(); }
60
61 private:
62 AutoEnable fAutoEnable;
63 GrAuditTrail* fAuditTrail;
64 };
65
66 class AutoCollectOps {
67 public:
68 AutoCollectOps(GrAuditTrail* auditTrail, int clientID)
69 : fAutoEnable(auditTrail), fAuditTrail(auditTrail) {
70 fAuditTrail->setClientID(clientID);
71 }
72
73 ~AutoCollectOps() { fAuditTrail->setClientID(kGrAuditTrailInvalidID); }
74
75 private:
76 AutoEnable fAutoEnable;
77 GrAuditTrail* fAuditTrail;
78 };
79
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
100 bool isEnabled() { return fEnabled; }
101 void setEnabled(bool enabled) { fEnabled = enabled; }
102
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
125private:
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 {
141 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