blob: 411be3f42812d2f1b48d4085e36395cd6196d882 [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 Phillips318c4192017-05-17 09:36:38 -040013void GrAuditTrail::addOp(const GrOp* op, GrRenderTargetProxy::UniqueID proxyID) {
joshualittdf3f2b02016-03-01 07:47:56 -080014 SkASSERT(fEnabled);
Brian Salomon42ad83a2016-12-20 16:14:45 -050015 Op* auditOp = new Op;
16 fOpPool.emplace_back(auditOp);
17 auditOp->fName = op->name();
18 auditOp->fBounds = op->bounds();
19 auditOp->fClientID = kGrAuditTrailInvalidID;
20 auditOp->fOpListID = kGrAuditTrailInvalidID;
21 auditOp->fChildID = kGrAuditTrailInvalidID;
halcanary9d524f22016-03-29 09:03:52 -070022
joshualittf55c3642016-03-02 08:11:34 -080023 // consume the current stack trace if any
Brian Salomon42ad83a2016-12-20 16:14:45 -050024 auditOp->fStackTrace = fCurrentStackTrace;
joshualittf55c3642016-03-02 08:11:34 -080025 fCurrentStackTrace.reset();
halcanary9d524f22016-03-29 09:03:52 -070026
joshualittdf3f2b02016-03-01 07:47:56 -080027 if (fClientID != kGrAuditTrailInvalidID) {
Brian Salomon42ad83a2016-12-20 16:14:45 -050028 auditOp->fClientID = fClientID;
29 Ops** opsLookup = fClientIDLookup.find(fClientID);
30 Ops* ops = nullptr;
31 if (!opsLookup) {
32 ops = new Ops;
33 fClientIDLookup.set(fClientID, ops);
joshualittdf3f2b02016-03-01 07:47:56 -080034 } else {
Brian Salomon42ad83a2016-12-20 16:14:45 -050035 ops = *opsLookup;
joshualittdf3f2b02016-03-01 07:47:56 -080036 }
37
Brian Salomon42ad83a2016-12-20 16:14:45 -050038 ops->push_back(auditOp);
joshualittdf3f2b02016-03-01 07:47:56 -080039 }
joshualittdf3f2b02016-03-01 07:47:56 -080040
Brian Salomon42ad83a2016-12-20 16:14:45 -050041 // Our algorithm doesn't bother to reorder inside of an OpNode so the ChildID will start at 0
42 auditOp->fOpListID = fOpList.count();
43 auditOp->fChildID = 0;
joshualittb95c7722016-02-29 07:44:02 -080044
Brian Salomon42ad83a2016-12-20 16:14:45 -050045 // We use the op pointer as a key to find the OpNode we are 'glomming' ops onto
46 fIDLookup.set(op->uniqueID(), auditOp->fOpListID);
Robert Phillips318c4192017-05-17 09:36:38 -040047 OpNode* opNode = new OpNode(proxyID);
Brian Salomon42ad83a2016-12-20 16:14:45 -050048 opNode->fBounds = op->bounds();
49 opNode->fChildren.push_back(auditOp);
50 fOpList.emplace_back(opNode);
joshualitt18d6b752016-02-26 08:07:50 -080051}
52
Brian Salomon42ad83a2016-12-20 16:14:45 -050053void GrAuditTrail::opsCombined(const GrOp* consumer, const GrOp* consumed) {
54 // Look up the op we are going to glom onto
joshualittb0666ad2016-03-08 10:43:41 -080055 int* indexPtr = fIDLookup.find(consumer->uniqueID());
56 SkASSERT(indexPtr);
57 int index = *indexPtr;
Brian Salomon42ad83a2016-12-20 16:14:45 -050058 SkASSERT(index < fOpList.count() && fOpList[index]);
59 OpNode& consumerOp = *fOpList[index];
joshualittb0666ad2016-03-08 10:43:41 -080060
Brian Salomon42ad83a2016-12-20 16:14:45 -050061 // Look up the op which will be glommed
joshualittb0666ad2016-03-08 10:43:41 -080062 int* consumedPtr = fIDLookup.find(consumed->uniqueID());
63 SkASSERT(consumedPtr);
64 int consumedIndex = *consumedPtr;
Brian Salomon42ad83a2016-12-20 16:14:45 -050065 SkASSERT(consumedIndex < fOpList.count() && fOpList[consumedIndex]);
66 OpNode& consumedOp = *fOpList[consumedIndex];
joshualittb0666ad2016-03-08 10:43:41 -080067
Brian Salomon42ad83a2016-12-20 16:14:45 -050068 // steal all of consumed's ops
69 for (int i = 0; i < consumedOp.fChildren.count(); i++) {
70 Op* childOp = consumedOp.fChildren[i];
halcanary9d524f22016-03-29 09:03:52 -070071
Brian Salomon42ad83a2016-12-20 16:14:45 -050072 // set the ids for the child op
73 childOp->fOpListID = index;
74 childOp->fChildID = consumerOp.fChildren.count();
75 consumerOp.fChildren.push_back(childOp);
joshualittb0666ad2016-03-08 10:43:41 -080076 }
halcanary9d524f22016-03-29 09:03:52 -070077
joshualittb0666ad2016-03-08 10:43:41 -080078 // Update the bounds for the combineWith node
Brian Salomon42ad83a2016-12-20 16:14:45 -050079 consumerOp.fBounds = consumer->bounds();
joshualittb0666ad2016-03-08 10:43:41 -080080
Brian Salomon42ad83a2016-12-20 16:14:45 -050081 // remove the old node from our opList and clear the combinee's lookup
82 // NOTE: because we can't change the shape of the oplist, we use a sentinel
83 fOpList[consumedIndex].reset(nullptr);
joshualittb0666ad2016-03-08 10:43:41 -080084 fIDLookup.remove(consumed->uniqueID());
85}
86
Brian Salomon42ad83a2016-12-20 16:14:45 -050087void GrAuditTrail::copyOutFromOpList(OpInfo* outOpInfo, int opListID) {
88 SkASSERT(opListID < fOpList.count());
89 const OpNode* bn = fOpList[opListID].get();
joshualittb0666ad2016-03-08 10:43:41 -080090 SkASSERT(bn);
Brian Salomon42ad83a2016-12-20 16:14:45 -050091 outOpInfo->fBounds = bn->fBounds;
Robert Phillipsf7a72612017-03-31 10:03:45 -040092 outOpInfo->fProxyUniqueID = bn->fProxyUniqueID;
joshualitt46b301d2016-03-02 08:32:37 -080093 for (int j = 0; j < bn->fChildren.count(); j++) {
Brian Salomon42ad83a2016-12-20 16:14:45 -050094 OpInfo::Op& outOp = outOpInfo->fOps.push_back();
95 const Op* currentOp = bn->fChildren[j];
96 outOp.fBounds = currentOp->fBounds;
97 outOp.fClientID = currentOp->fClientID;
joshualitt46b301d2016-03-02 08:32:37 -080098 }
99}
100
Brian Salomon42ad83a2016-12-20 16:14:45 -0500101void GrAuditTrail::getBoundsByClientID(SkTArray<OpInfo>* outInfo, int clientID) {
102 Ops** opsLookup = fClientIDLookup.find(clientID);
103 if (opsLookup) {
104 // We track which oplistID we're currently looking at. If it changes, then we need to push
105 // back a new op info struct. We happen to know that ops are in sequential order in the
106 // oplist, otherwise we'd have to do more bookkeeping
107 int currentOpListID = kGrAuditTrailInvalidID;
108 for (int i = 0; i < (*opsLookup)->count(); i++) {
109 const Op* op = (**opsLookup)[i];
joshualitt10d8fc22016-02-29 11:15:06 -0800110
Brian Salomon42ad83a2016-12-20 16:14:45 -0500111 // Because we will copy out all of the ops associated with a given op list id everytime
112 // the id changes, we only have to update our struct when the id changes.
113 if (kGrAuditTrailInvalidID == currentOpListID || op->fOpListID != currentOpListID) {
114 OpInfo& outOpInfo = outInfo->push_back();
halcanary9d524f22016-03-29 09:03:52 -0700115
Brian Salomon42ad83a2016-12-20 16:14:45 -0500116 // copy out all of the ops so the client can display them even if they have a
117 // different clientID
118 this->copyOutFromOpList(&outOpInfo, op->fOpListID);
joshualitt10d8fc22016-02-29 11:15:06 -0800119 }
120 }
121 }
122}
123
Brian Salomon42ad83a2016-12-20 16:14:45 -0500124void GrAuditTrail::getBoundsByOpListID(OpInfo* outInfo, int opListID) {
125 this->copyOutFromOpList(outInfo, opListID);
joshualitt46b301d2016-03-02 08:32:37 -0800126}
127
joshualittdf3f2b02016-03-01 07:47:56 -0800128void GrAuditTrail::fullReset() {
129 SkASSERT(fEnabled);
Brian Salomon42ad83a2016-12-20 16:14:45 -0500130 fOpList.reset();
joshualittdf3f2b02016-03-01 07:47:56 -0800131 fIDLookup.reset();
Brian Salomon42ad83a2016-12-20 16:14:45 -0500132 // free all client ops
133 fClientIDLookup.foreach ([](const int&, Ops** ops) { delete *ops; });
joshualittdf3f2b02016-03-01 07:47:56 -0800134 fClientIDLookup.reset();
Brian Salomon42ad83a2016-12-20 16:14:45 -0500135 fOpPool.reset(); // must be last, frees all of the memory
joshualittdf3f2b02016-03-01 07:47:56 -0800136}
joshualitt10d8fc22016-02-29 11:15:06 -0800137
joshualitt18d6b752016-02-26 08:07:50 -0800138template <typename T>
139void GrAuditTrail::JsonifyTArray(SkString* json, const char* name, const T& array,
joshualittadab5a22016-02-18 05:04:39 -0800140 bool addComma) {
joshualitt87a721b2016-01-12 12:59:28 -0800141 if (array.count()) {
joshualittadab5a22016-02-18 05:04:39 -0800142 if (addComma) {
143 json->appendf(",");
144 }
joshualitt87a721b2016-01-12 12:59:28 -0800145 json->appendf("\"%s\": [", name);
joshualittae47aee2016-03-10 13:29:36 -0800146 const char* separator = "";
joshualitt87a721b2016-01-12 12:59:28 -0800147 for (int i = 0; i < array.count(); i++) {
joshualittb0666ad2016-03-08 10:43:41 -0800148 // Handle sentinel nullptrs
joshualittae47aee2016-03-10 13:29:36 -0800149 if (array[i]) {
150 json->appendf("%s", separator);
151 json->append(array[i]->toJson());
152 separator = ",";
joshualitt87a721b2016-01-12 12:59:28 -0800153 }
joshualitt27a48dc2016-01-08 07:19:47 -0800154 }
joshualitt87a721b2016-01-12 12:59:28 -0800155 json->append("]");
joshualitt27a48dc2016-01-08 07:19:47 -0800156 }
joshualitt086cee12016-01-12 06:45:24 -0800157}
158
159// This will pretty print a very small subset of json
160// The parsing rules are straightforward, aside from the fact that we do not want an extra newline
161// before ',' and after '}', so we have a comma exception rule.
162class PrettyPrintJson {
163public:
164 SkString prettify(const SkString& json) {
165 fPrettyJson.reset();
166 fTabCount = 0;
167 fFreshLine = false;
168 fCommaException = false;
169 for (size_t i = 0; i < json.size(); i++) {
170 if ('[' == json[i] || '{' == json[i]) {
171 this->newline();
172 this->appendChar(json[i]);
173 fTabCount++;
174 this->newline();
175 } else if (']' == json[i] || '}' == json[i]) {
176 fTabCount--;
177 this->newline();
178 this->appendChar(json[i]);
179 fCommaException = true;
180 } else if (',' == json[i]) {
181 this->appendChar(json[i]);
182 this->newline();
183 } else {
184 this->appendChar(json[i]);
185 }
186 }
187 return fPrettyJson;
188 }
189private:
190 void appendChar(char appendee) {
191 if (fCommaException && ',' != appendee) {
192 this->newline();
193 }
194 this->tab();
195 fPrettyJson += appendee;
196 fFreshLine = false;
197 fCommaException = false;
198 }
199
200 void tab() {
201 if (fFreshLine) {
202 for (int i = 0; i < fTabCount; i++) {
203 fPrettyJson += '\t';
204 }
205 }
206 }
207
208 void newline() {
209 if (!fFreshLine) {
210 fFreshLine = true;
211 fPrettyJson += '\n';
212 }
213 }
214
215 SkString fPrettyJson;
216 int fTabCount;
217 bool fFreshLine;
218 bool fCommaException;
219};
220
221static SkString pretty_print_json(SkString json) {
222 class PrettyPrintJson prettyPrintJson;
223 return prettyPrintJson.prettify(json);
224}
225
joshualittb95c7722016-02-29 07:44:02 -0800226SkString GrAuditTrail::toJson(bool prettyPrint) const {
joshualitt086cee12016-01-12 06:45:24 -0800227 SkString json;
228 json.append("{");
Brian Salomonf09492b2016-12-21 15:40:26 -0500229 JsonifyTArray(&json, "Ops", fOpList, false);
joshualitt086cee12016-01-12 06:45:24 -0800230 json.append("}");
231
joshualitt6b3cf732016-02-17 11:20:26 -0800232 if (prettyPrint) {
233 return pretty_print_json(json);
234 } else {
235 return json;
236 }
joshualitt27a48dc2016-01-08 07:19:47 -0800237}
238
joshualittb95c7722016-02-29 07:44:02 -0800239SkString GrAuditTrail::toJson(int clientID, bool prettyPrint) const {
joshualitt27a48dc2016-01-08 07:19:47 -0800240 SkString json;
joshualitt086cee12016-01-12 06:45:24 -0800241 json.append("{");
Brian Salomon42ad83a2016-12-20 16:14:45 -0500242 Ops** ops = fClientIDLookup.find(clientID);
243 if (ops) {
Brian Salomonf09492b2016-12-21 15:40:26 -0500244 JsonifyTArray(&json, "Ops", **ops, false);
joshualittb95c7722016-02-29 07:44:02 -0800245 }
246 json.appendf("}");
247
248 if (prettyPrint) {
249 return pretty_print_json(json);
250 } else {
251 return json;
252 }
253}
254
255static void skrect_to_json(SkString* json, const char* name, const SkRect& rect) {
256 json->appendf("\"%s\": {", name);
257 json->appendf("\"Left\": %f,", rect.fLeft);
258 json->appendf("\"Right\": %f,", rect.fRight);
259 json->appendf("\"Top\": %f,", rect.fTop);
260 json->appendf("\"Bottom\": %f", rect.fBottom);
261 json->append("}");
joshualitt27a48dc2016-01-08 07:19:47 -0800262}
263
Brian Salomon42ad83a2016-12-20 16:14:45 -0500264SkString GrAuditTrail::Op::toJson() const {
joshualitt086cee12016-01-12 06:45:24 -0800265 SkString json;
266 json.append("{");
joshualittb95c7722016-02-29 07:44:02 -0800267 json.appendf("\"Name\": \"%s\",", fName.c_str());
268 json.appendf("\"ClientID\": \"%d\",", fClientID);
Brian Salomonf09492b2016-12-21 15:40:26 -0500269 json.appendf("\"OpListID\": \"%d\",", fOpListID);
joshualittb95c7722016-02-29 07:44:02 -0800270 json.appendf("\"ChildID\": \"%d\",", fChildID);
271 skrect_to_json(&json, "Bounds", fBounds);
joshualittf55c3642016-03-02 08:11:34 -0800272 if (fStackTrace.count()) {
273 json.append(",\"Stack\": [");
274 for (int i = 0; i < fStackTrace.count(); i++) {
275 json.appendf("\"%s\"", fStackTrace[i].c_str());
276 if (i < fStackTrace.count() - 1) {
277 json.append(",");
278 }
279 }
280 json.append("]");
281 }
joshualitt086cee12016-01-12 06:45:24 -0800282 json.append("}");
joshualittb95c7722016-02-29 07:44:02 -0800283 return json;
284}
285
Brian Salomon42ad83a2016-12-20 16:14:45 -0500286SkString GrAuditTrail::OpNode::toJson() const {
joshualittb95c7722016-02-29 07:44:02 -0800287 SkString json;
288 json.append("{");
Robert Phillipsf7a72612017-03-31 10:03:45 -0400289 json.appendf("\"ProxyID\": \"%u\",", fProxyUniqueID.asUInt());
joshualittb95c7722016-02-29 07:44:02 -0800290 skrect_to_json(&json, "Bounds", fBounds);
Brian Salomonf09492b2016-12-21 15:40:26 -0500291 JsonifyTArray(&json, "Ops", fChildren, true);
joshualitt086cee12016-01-12 06:45:24 -0800292 json.append("}");
293 return json;
294}