blob: e6b5c3adb2bdf593bc72b2de69e4e1799b09b8ca [file] [log] [blame]
joshualitt27a48dc2016-01-08 07:19:47 -08001/*
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#include "GrAuditTrail.h"
Brian Salomon89527432016-12-16 09:52:16 -05009#include "ops/GrOp.h"
joshualitt27a48dc2016-01-08 07:19:47 -080010
joshualittb95c7722016-02-29 07:44:02 -080011const int GrAuditTrail::kGrAuditTrailInvalidID = -1;
12
Robert Phillipsf7a72612017-03-31 10:03:45 -040013void GrAuditTrail::addOp(const GrOp* op,
14 GrGpuResource::UniqueID resourceID,
15 GrRenderTargetProxy::UniqueID proxyID) {
joshualittdf3f2b02016-03-01 07:47:56 -080016 SkASSERT(fEnabled);
Brian Salomon42ad83a2016-12-20 16:14:45 -050017 Op* auditOp = new Op;
18 fOpPool.emplace_back(auditOp);
19 auditOp->fName = op->name();
20 auditOp->fBounds = op->bounds();
21 auditOp->fClientID = kGrAuditTrailInvalidID;
22 auditOp->fOpListID = kGrAuditTrailInvalidID;
23 auditOp->fChildID = kGrAuditTrailInvalidID;
halcanary9d524f22016-03-29 09:03:52 -070024
joshualittf55c3642016-03-02 08:11:34 -080025 // consume the current stack trace if any
Brian Salomon42ad83a2016-12-20 16:14:45 -050026 auditOp->fStackTrace = fCurrentStackTrace;
joshualittf55c3642016-03-02 08:11:34 -080027 fCurrentStackTrace.reset();
halcanary9d524f22016-03-29 09:03:52 -070028
joshualittdf3f2b02016-03-01 07:47:56 -080029 if (fClientID != kGrAuditTrailInvalidID) {
Brian Salomon42ad83a2016-12-20 16:14:45 -050030 auditOp->fClientID = fClientID;
31 Ops** opsLookup = fClientIDLookup.find(fClientID);
32 Ops* ops = nullptr;
33 if (!opsLookup) {
34 ops = new Ops;
35 fClientIDLookup.set(fClientID, ops);
joshualittdf3f2b02016-03-01 07:47:56 -080036 } else {
Brian Salomon42ad83a2016-12-20 16:14:45 -050037 ops = *opsLookup;
joshualittdf3f2b02016-03-01 07:47:56 -080038 }
39
Brian Salomon42ad83a2016-12-20 16:14:45 -050040 ops->push_back(auditOp);
joshualittdf3f2b02016-03-01 07:47:56 -080041 }
joshualittdf3f2b02016-03-01 07:47:56 -080042
Brian Salomon42ad83a2016-12-20 16:14:45 -050043 // Our algorithm doesn't bother to reorder inside of an OpNode so the ChildID will start at 0
44 auditOp->fOpListID = fOpList.count();
45 auditOp->fChildID = 0;
joshualittb95c7722016-02-29 07:44:02 -080046
Brian Salomon42ad83a2016-12-20 16:14:45 -050047 // We use the op pointer as a key to find the OpNode we are 'glomming' ops onto
48 fIDLookup.set(op->uniqueID(), auditOp->fOpListID);
Robert Phillipsf7a72612017-03-31 10:03:45 -040049 OpNode* opNode = new OpNode(resourceID, proxyID);
Brian Salomon42ad83a2016-12-20 16:14:45 -050050 opNode->fBounds = op->bounds();
51 opNode->fChildren.push_back(auditOp);
52 fOpList.emplace_back(opNode);
joshualitt18d6b752016-02-26 08:07:50 -080053}
54
Brian Salomon42ad83a2016-12-20 16:14:45 -050055void GrAuditTrail::opsCombined(const GrOp* consumer, const GrOp* consumed) {
56 // Look up the op we are going to glom onto
joshualittb0666ad2016-03-08 10:43:41 -080057 int* indexPtr = fIDLookup.find(consumer->uniqueID());
58 SkASSERT(indexPtr);
59 int index = *indexPtr;
Brian Salomon42ad83a2016-12-20 16:14:45 -050060 SkASSERT(index < fOpList.count() && fOpList[index]);
61 OpNode& consumerOp = *fOpList[index];
joshualittb0666ad2016-03-08 10:43:41 -080062
Brian Salomon42ad83a2016-12-20 16:14:45 -050063 // Look up the op which will be glommed
joshualittb0666ad2016-03-08 10:43:41 -080064 int* consumedPtr = fIDLookup.find(consumed->uniqueID());
65 SkASSERT(consumedPtr);
66 int consumedIndex = *consumedPtr;
Brian Salomon42ad83a2016-12-20 16:14:45 -050067 SkASSERT(consumedIndex < fOpList.count() && fOpList[consumedIndex]);
68 OpNode& consumedOp = *fOpList[consumedIndex];
joshualittb0666ad2016-03-08 10:43:41 -080069
Brian Salomon42ad83a2016-12-20 16:14:45 -050070 // steal all of consumed's ops
71 for (int i = 0; i < consumedOp.fChildren.count(); i++) {
72 Op* childOp = consumedOp.fChildren[i];
halcanary9d524f22016-03-29 09:03:52 -070073
Brian Salomon42ad83a2016-12-20 16:14:45 -050074 // set the ids for the child op
75 childOp->fOpListID = index;
76 childOp->fChildID = consumerOp.fChildren.count();
77 consumerOp.fChildren.push_back(childOp);
joshualittb0666ad2016-03-08 10:43:41 -080078 }
halcanary9d524f22016-03-29 09:03:52 -070079
joshualittb0666ad2016-03-08 10:43:41 -080080 // Update the bounds for the combineWith node
Brian Salomon42ad83a2016-12-20 16:14:45 -050081 consumerOp.fBounds = consumer->bounds();
joshualittb0666ad2016-03-08 10:43:41 -080082
Brian Salomon42ad83a2016-12-20 16:14:45 -050083 // remove the old node from our opList and clear the combinee's lookup
84 // NOTE: because we can't change the shape of the oplist, we use a sentinel
85 fOpList[consumedIndex].reset(nullptr);
joshualittb0666ad2016-03-08 10:43:41 -080086 fIDLookup.remove(consumed->uniqueID());
87}
88
Brian Salomon42ad83a2016-12-20 16:14:45 -050089void GrAuditTrail::copyOutFromOpList(OpInfo* outOpInfo, int opListID) {
90 SkASSERT(opListID < fOpList.count());
91 const OpNode* bn = fOpList[opListID].get();
joshualittb0666ad2016-03-08 10:43:41 -080092 SkASSERT(bn);
Brian Salomon42ad83a2016-12-20 16:14:45 -050093 outOpInfo->fBounds = bn->fBounds;
Robert Phillipsf7a72612017-03-31 10:03:45 -040094 outOpInfo->fResourceUniqueID = bn->fResourceUniqueID;
95 outOpInfo->fProxyUniqueID = bn->fProxyUniqueID;
joshualitt46b301d2016-03-02 08:32:37 -080096 for (int j = 0; j < bn->fChildren.count(); j++) {
Brian Salomon42ad83a2016-12-20 16:14:45 -050097 OpInfo::Op& outOp = outOpInfo->fOps.push_back();
98 const Op* currentOp = bn->fChildren[j];
99 outOp.fBounds = currentOp->fBounds;
100 outOp.fClientID = currentOp->fClientID;
joshualitt46b301d2016-03-02 08:32:37 -0800101 }
102}
103
Brian Salomon42ad83a2016-12-20 16:14:45 -0500104void GrAuditTrail::getBoundsByClientID(SkTArray<OpInfo>* outInfo, int clientID) {
105 Ops** opsLookup = fClientIDLookup.find(clientID);
106 if (opsLookup) {
107 // We track which oplistID we're currently looking at. If it changes, then we need to push
108 // back a new op info struct. We happen to know that ops are in sequential order in the
109 // oplist, otherwise we'd have to do more bookkeeping
110 int currentOpListID = kGrAuditTrailInvalidID;
111 for (int i = 0; i < (*opsLookup)->count(); i++) {
112 const Op* op = (**opsLookup)[i];
joshualitt10d8fc22016-02-29 11:15:06 -0800113
Brian Salomon42ad83a2016-12-20 16:14:45 -0500114 // Because we will copy out all of the ops associated with a given op list id everytime
115 // the id changes, we only have to update our struct when the id changes.
116 if (kGrAuditTrailInvalidID == currentOpListID || op->fOpListID != currentOpListID) {
117 OpInfo& outOpInfo = outInfo->push_back();
halcanary9d524f22016-03-29 09:03:52 -0700118
Brian Salomon42ad83a2016-12-20 16:14:45 -0500119 // copy out all of the ops so the client can display them even if they have a
120 // different clientID
121 this->copyOutFromOpList(&outOpInfo, op->fOpListID);
joshualitt10d8fc22016-02-29 11:15:06 -0800122 }
123 }
124 }
125}
126
Brian Salomon42ad83a2016-12-20 16:14:45 -0500127void GrAuditTrail::getBoundsByOpListID(OpInfo* outInfo, int opListID) {
128 this->copyOutFromOpList(outInfo, opListID);
joshualitt46b301d2016-03-02 08:32:37 -0800129}
130
joshualittdf3f2b02016-03-01 07:47:56 -0800131void GrAuditTrail::fullReset() {
132 SkASSERT(fEnabled);
Brian Salomon42ad83a2016-12-20 16:14:45 -0500133 fOpList.reset();
joshualittdf3f2b02016-03-01 07:47:56 -0800134 fIDLookup.reset();
Brian Salomon42ad83a2016-12-20 16:14:45 -0500135 // free all client ops
136 fClientIDLookup.foreach ([](const int&, Ops** ops) { delete *ops; });
joshualittdf3f2b02016-03-01 07:47:56 -0800137 fClientIDLookup.reset();
Brian Salomon42ad83a2016-12-20 16:14:45 -0500138 fOpPool.reset(); // must be last, frees all of the memory
joshualittdf3f2b02016-03-01 07:47:56 -0800139}
joshualitt10d8fc22016-02-29 11:15:06 -0800140
joshualitt18d6b752016-02-26 08:07:50 -0800141template <typename T>
142void GrAuditTrail::JsonifyTArray(SkString* json, const char* name, const T& array,
joshualittadab5a22016-02-18 05:04:39 -0800143 bool addComma) {
joshualitt87a721b2016-01-12 12:59:28 -0800144 if (array.count()) {
joshualittadab5a22016-02-18 05:04:39 -0800145 if (addComma) {
146 json->appendf(",");
147 }
joshualitt87a721b2016-01-12 12:59:28 -0800148 json->appendf("\"%s\": [", name);
joshualittae47aee2016-03-10 13:29:36 -0800149 const char* separator = "";
joshualitt87a721b2016-01-12 12:59:28 -0800150 for (int i = 0; i < array.count(); i++) {
joshualittb0666ad2016-03-08 10:43:41 -0800151 // Handle sentinel nullptrs
joshualittae47aee2016-03-10 13:29:36 -0800152 if (array[i]) {
153 json->appendf("%s", separator);
154 json->append(array[i]->toJson());
155 separator = ",";
joshualitt87a721b2016-01-12 12:59:28 -0800156 }
joshualitt27a48dc2016-01-08 07:19:47 -0800157 }
joshualitt87a721b2016-01-12 12:59:28 -0800158 json->append("]");
joshualitt27a48dc2016-01-08 07:19:47 -0800159 }
joshualitt086cee12016-01-12 06:45:24 -0800160}
161
162// This will pretty print a very small subset of json
163// The parsing rules are straightforward, aside from the fact that we do not want an extra newline
164// before ',' and after '}', so we have a comma exception rule.
165class PrettyPrintJson {
166public:
167 SkString prettify(const SkString& json) {
168 fPrettyJson.reset();
169 fTabCount = 0;
170 fFreshLine = false;
171 fCommaException = false;
172 for (size_t i = 0; i < json.size(); i++) {
173 if ('[' == json[i] || '{' == json[i]) {
174 this->newline();
175 this->appendChar(json[i]);
176 fTabCount++;
177 this->newline();
178 } else if (']' == json[i] || '}' == json[i]) {
179 fTabCount--;
180 this->newline();
181 this->appendChar(json[i]);
182 fCommaException = true;
183 } else if (',' == json[i]) {
184 this->appendChar(json[i]);
185 this->newline();
186 } else {
187 this->appendChar(json[i]);
188 }
189 }
190 return fPrettyJson;
191 }
192private:
193 void appendChar(char appendee) {
194 if (fCommaException && ',' != appendee) {
195 this->newline();
196 }
197 this->tab();
198 fPrettyJson += appendee;
199 fFreshLine = false;
200 fCommaException = false;
201 }
202
203 void tab() {
204 if (fFreshLine) {
205 for (int i = 0; i < fTabCount; i++) {
206 fPrettyJson += '\t';
207 }
208 }
209 }
210
211 void newline() {
212 if (!fFreshLine) {
213 fFreshLine = true;
214 fPrettyJson += '\n';
215 }
216 }
217
218 SkString fPrettyJson;
219 int fTabCount;
220 bool fFreshLine;
221 bool fCommaException;
222};
223
224static SkString pretty_print_json(SkString json) {
225 class PrettyPrintJson prettyPrintJson;
226 return prettyPrintJson.prettify(json);
227}
228
joshualittb95c7722016-02-29 07:44:02 -0800229SkString GrAuditTrail::toJson(bool prettyPrint) const {
joshualitt086cee12016-01-12 06:45:24 -0800230 SkString json;
231 json.append("{");
Brian Salomonf09492b2016-12-21 15:40:26 -0500232 JsonifyTArray(&json, "Ops", fOpList, false);
joshualitt086cee12016-01-12 06:45:24 -0800233 json.append("}");
234
joshualitt6b3cf732016-02-17 11:20:26 -0800235 if (prettyPrint) {
236 return pretty_print_json(json);
237 } else {
238 return json;
239 }
joshualitt27a48dc2016-01-08 07:19:47 -0800240}
241
joshualittb95c7722016-02-29 07:44:02 -0800242SkString GrAuditTrail::toJson(int clientID, bool prettyPrint) const {
joshualitt27a48dc2016-01-08 07:19:47 -0800243 SkString json;
joshualitt086cee12016-01-12 06:45:24 -0800244 json.append("{");
Brian Salomon42ad83a2016-12-20 16:14:45 -0500245 Ops** ops = fClientIDLookup.find(clientID);
246 if (ops) {
Brian Salomonf09492b2016-12-21 15:40:26 -0500247 JsonifyTArray(&json, "Ops", **ops, false);
joshualittb95c7722016-02-29 07:44:02 -0800248 }
249 json.appendf("}");
250
251 if (prettyPrint) {
252 return pretty_print_json(json);
253 } else {
254 return json;
255 }
256}
257
258static void skrect_to_json(SkString* json, const char* name, const SkRect& rect) {
259 json->appendf("\"%s\": {", name);
260 json->appendf("\"Left\": %f,", rect.fLeft);
261 json->appendf("\"Right\": %f,", rect.fRight);
262 json->appendf("\"Top\": %f,", rect.fTop);
263 json->appendf("\"Bottom\": %f", rect.fBottom);
264 json->append("}");
joshualitt27a48dc2016-01-08 07:19:47 -0800265}
266
Brian Salomon42ad83a2016-12-20 16:14:45 -0500267SkString GrAuditTrail::Op::toJson() const {
joshualitt086cee12016-01-12 06:45:24 -0800268 SkString json;
269 json.append("{");
joshualittb95c7722016-02-29 07:44:02 -0800270 json.appendf("\"Name\": \"%s\",", fName.c_str());
271 json.appendf("\"ClientID\": \"%d\",", fClientID);
Brian Salomonf09492b2016-12-21 15:40:26 -0500272 json.appendf("\"OpListID\": \"%d\",", fOpListID);
joshualittb95c7722016-02-29 07:44:02 -0800273 json.appendf("\"ChildID\": \"%d\",", fChildID);
274 skrect_to_json(&json, "Bounds", fBounds);
joshualittf55c3642016-03-02 08:11:34 -0800275 if (fStackTrace.count()) {
276 json.append(",\"Stack\": [");
277 for (int i = 0; i < fStackTrace.count(); i++) {
278 json.appendf("\"%s\"", fStackTrace[i].c_str());
279 if (i < fStackTrace.count() - 1) {
280 json.append(",");
281 }
282 }
283 json.append("]");
284 }
joshualitt086cee12016-01-12 06:45:24 -0800285 json.append("}");
joshualittb95c7722016-02-29 07:44:02 -0800286 return json;
287}
288
Brian Salomon42ad83a2016-12-20 16:14:45 -0500289SkString GrAuditTrail::OpNode::toJson() const {
joshualittb95c7722016-02-29 07:44:02 -0800290 SkString json;
291 json.append("{");
Robert Phillipsf7a72612017-03-31 10:03:45 -0400292 json.appendf("\"ResourceID\": \"%u\",", fResourceUniqueID.asUInt());
293 json.appendf("\"ProxyID\": \"%u\",", fProxyUniqueID.asUInt());
joshualittb95c7722016-02-29 07:44:02 -0800294 skrect_to_json(&json, "Bounds", fBounds);
Brian Salomonf09492b2016-12-21 15:40:26 -0500295 JsonifyTArray(&json, "Ops", fChildren, true);
joshualitt086cee12016-01-12 06:45:24 -0800296 json.append("}");
297 return json;
298}