Line data Source code
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 : #include "GrAuditTrail.h"
9 : #include "ops/GrOp.h"
10 :
11 : const int GrAuditTrail::kGrAuditTrailInvalidID = -1;
12 :
13 0 : void GrAuditTrail::addOp(const GrOp* op,
14 : GrGpuResource::UniqueID resourceID,
15 : GrRenderTargetProxy::UniqueID proxyID) {
16 0 : SkASSERT(fEnabled);
17 0 : Op* auditOp = new Op;
18 0 : fOpPool.emplace_back(auditOp);
19 0 : auditOp->fName = op->name();
20 0 : auditOp->fBounds = op->bounds();
21 0 : auditOp->fClientID = kGrAuditTrailInvalidID;
22 0 : auditOp->fOpListID = kGrAuditTrailInvalidID;
23 0 : auditOp->fChildID = kGrAuditTrailInvalidID;
24 :
25 : // consume the current stack trace if any
26 0 : auditOp->fStackTrace = fCurrentStackTrace;
27 0 : fCurrentStackTrace.reset();
28 :
29 0 : if (fClientID != kGrAuditTrailInvalidID) {
30 0 : auditOp->fClientID = fClientID;
31 0 : Ops** opsLookup = fClientIDLookup.find(fClientID);
32 0 : Ops* ops = nullptr;
33 0 : if (!opsLookup) {
34 0 : ops = new Ops;
35 0 : fClientIDLookup.set(fClientID, ops);
36 : } else {
37 0 : ops = *opsLookup;
38 : }
39 :
40 0 : ops->push_back(auditOp);
41 : }
42 :
43 : // Our algorithm doesn't bother to reorder inside of an OpNode so the ChildID will start at 0
44 0 : auditOp->fOpListID = fOpList.count();
45 0 : auditOp->fChildID = 0;
46 :
47 : // We use the op pointer as a key to find the OpNode we are 'glomming' ops onto
48 0 : fIDLookup.set(op->uniqueID(), auditOp->fOpListID);
49 0 : OpNode* opNode = new OpNode(resourceID, proxyID);
50 0 : opNode->fBounds = op->bounds();
51 0 : opNode->fChildren.push_back(auditOp);
52 0 : fOpList.emplace_back(opNode);
53 0 : }
54 :
55 0 : void GrAuditTrail::opsCombined(const GrOp* consumer, const GrOp* consumed) {
56 : // Look up the op we are going to glom onto
57 0 : int* indexPtr = fIDLookup.find(consumer->uniqueID());
58 0 : SkASSERT(indexPtr);
59 0 : int index = *indexPtr;
60 0 : SkASSERT(index < fOpList.count() && fOpList[index]);
61 0 : OpNode& consumerOp = *fOpList[index];
62 :
63 : // Look up the op which will be glommed
64 0 : int* consumedPtr = fIDLookup.find(consumed->uniqueID());
65 0 : SkASSERT(consumedPtr);
66 0 : int consumedIndex = *consumedPtr;
67 0 : SkASSERT(consumedIndex < fOpList.count() && fOpList[consumedIndex]);
68 0 : OpNode& consumedOp = *fOpList[consumedIndex];
69 :
70 : // steal all of consumed's ops
71 0 : for (int i = 0; i < consumedOp.fChildren.count(); i++) {
72 0 : Op* childOp = consumedOp.fChildren[i];
73 :
74 : // set the ids for the child op
75 0 : childOp->fOpListID = index;
76 0 : childOp->fChildID = consumerOp.fChildren.count();
77 0 : consumerOp.fChildren.push_back(childOp);
78 : }
79 :
80 : // Update the bounds for the combineWith node
81 0 : consumerOp.fBounds = consumer->bounds();
82 :
83 : // 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 0 : fOpList[consumedIndex].reset(nullptr);
86 0 : fIDLookup.remove(consumed->uniqueID());
87 0 : }
88 :
89 0 : void GrAuditTrail::copyOutFromOpList(OpInfo* outOpInfo, int opListID) {
90 0 : SkASSERT(opListID < fOpList.count());
91 0 : const OpNode* bn = fOpList[opListID].get();
92 0 : SkASSERT(bn);
93 0 : outOpInfo->fBounds = bn->fBounds;
94 0 : outOpInfo->fResourceUniqueID = bn->fResourceUniqueID;
95 0 : outOpInfo->fProxyUniqueID = bn->fProxyUniqueID;
96 0 : for (int j = 0; j < bn->fChildren.count(); j++) {
97 0 : OpInfo::Op& outOp = outOpInfo->fOps.push_back();
98 0 : const Op* currentOp = bn->fChildren[j];
99 0 : outOp.fBounds = currentOp->fBounds;
100 0 : outOp.fClientID = currentOp->fClientID;
101 : }
102 0 : }
103 :
104 0 : void GrAuditTrail::getBoundsByClientID(SkTArray<OpInfo>* outInfo, int clientID) {
105 0 : Ops** opsLookup = fClientIDLookup.find(clientID);
106 0 : 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 0 : int currentOpListID = kGrAuditTrailInvalidID;
111 0 : for (int i = 0; i < (*opsLookup)->count(); i++) {
112 0 : const Op* op = (**opsLookup)[i];
113 :
114 : // 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 0 : if (kGrAuditTrailInvalidID == currentOpListID || op->fOpListID != currentOpListID) {
117 0 : OpInfo& outOpInfo = outInfo->push_back();
118 :
119 : // copy out all of the ops so the client can display them even if they have a
120 : // different clientID
121 0 : this->copyOutFromOpList(&outOpInfo, op->fOpListID);
122 : }
123 : }
124 : }
125 0 : }
126 :
127 0 : void GrAuditTrail::getBoundsByOpListID(OpInfo* outInfo, int opListID) {
128 0 : this->copyOutFromOpList(outInfo, opListID);
129 0 : }
130 :
131 0 : void GrAuditTrail::fullReset() {
132 0 : SkASSERT(fEnabled);
133 0 : fOpList.reset();
134 0 : fIDLookup.reset();
135 : // free all client ops
136 0 : fClientIDLookup.foreach ([](const int&, Ops** ops) { delete *ops; });
137 0 : fClientIDLookup.reset();
138 0 : fOpPool.reset(); // must be last, frees all of the memory
139 0 : }
140 :
141 : template <typename T>
142 0 : void GrAuditTrail::JsonifyTArray(SkString* json, const char* name, const T& array,
143 : bool addComma) {
144 0 : if (array.count()) {
145 0 : if (addComma) {
146 0 : json->appendf(",");
147 : }
148 0 : json->appendf("\"%s\": [", name);
149 0 : const char* separator = "";
150 0 : for (int i = 0; i < array.count(); i++) {
151 : // Handle sentinel nullptrs
152 0 : if (array[i]) {
153 0 : json->appendf("%s", separator);
154 0 : json->append(array[i]->toJson());
155 0 : separator = ",";
156 : }
157 : }
158 0 : json->append("]");
159 : }
160 0 : }
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.
165 0 : class PrettyPrintJson {
166 : public:
167 0 : SkString prettify(const SkString& json) {
168 0 : fPrettyJson.reset();
169 0 : fTabCount = 0;
170 0 : fFreshLine = false;
171 0 : fCommaException = false;
172 0 : for (size_t i = 0; i < json.size(); i++) {
173 0 : if ('[' == json[i] || '{' == json[i]) {
174 0 : this->newline();
175 0 : this->appendChar(json[i]);
176 0 : fTabCount++;
177 0 : this->newline();
178 0 : } else if (']' == json[i] || '}' == json[i]) {
179 0 : fTabCount--;
180 0 : this->newline();
181 0 : this->appendChar(json[i]);
182 0 : fCommaException = true;
183 0 : } else if (',' == json[i]) {
184 0 : this->appendChar(json[i]);
185 0 : this->newline();
186 : } else {
187 0 : this->appendChar(json[i]);
188 : }
189 : }
190 0 : return fPrettyJson;
191 : }
192 : private:
193 0 : void appendChar(char appendee) {
194 0 : if (fCommaException && ',' != appendee) {
195 0 : this->newline();
196 : }
197 0 : this->tab();
198 0 : fPrettyJson += appendee;
199 0 : fFreshLine = false;
200 0 : fCommaException = false;
201 0 : }
202 :
203 0 : void tab() {
204 0 : if (fFreshLine) {
205 0 : for (int i = 0; i < fTabCount; i++) {
206 0 : fPrettyJson += '\t';
207 : }
208 : }
209 0 : }
210 :
211 0 : void newline() {
212 0 : if (!fFreshLine) {
213 0 : fFreshLine = true;
214 0 : fPrettyJson += '\n';
215 : }
216 0 : }
217 :
218 : SkString fPrettyJson;
219 : int fTabCount;
220 : bool fFreshLine;
221 : bool fCommaException;
222 : };
223 :
224 0 : static SkString pretty_print_json(SkString json) {
225 0 : class PrettyPrintJson prettyPrintJson;
226 0 : return prettyPrintJson.prettify(json);
227 : }
228 :
229 0 : SkString GrAuditTrail::toJson(bool prettyPrint) const {
230 0 : SkString json;
231 0 : json.append("{");
232 0 : JsonifyTArray(&json, "Ops", fOpList, false);
233 0 : json.append("}");
234 :
235 0 : if (prettyPrint) {
236 0 : return pretty_print_json(json);
237 : } else {
238 0 : return json;
239 : }
240 : }
241 :
242 0 : SkString GrAuditTrail::toJson(int clientID, bool prettyPrint) const {
243 0 : SkString json;
244 0 : json.append("{");
245 0 : Ops** ops = fClientIDLookup.find(clientID);
246 0 : if (ops) {
247 0 : JsonifyTArray(&json, "Ops", **ops, false);
248 : }
249 0 : json.appendf("}");
250 :
251 0 : if (prettyPrint) {
252 0 : return pretty_print_json(json);
253 : } else {
254 0 : return json;
255 : }
256 : }
257 :
258 0 : static void skrect_to_json(SkString* json, const char* name, const SkRect& rect) {
259 0 : json->appendf("\"%s\": {", name);
260 0 : json->appendf("\"Left\": %f,", rect.fLeft);
261 0 : json->appendf("\"Right\": %f,", rect.fRight);
262 0 : json->appendf("\"Top\": %f,", rect.fTop);
263 0 : json->appendf("\"Bottom\": %f", rect.fBottom);
264 0 : json->append("}");
265 0 : }
266 :
267 0 : SkString GrAuditTrail::Op::toJson() const {
268 0 : SkString json;
269 0 : json.append("{");
270 0 : json.appendf("\"Name\": \"%s\",", fName.c_str());
271 0 : json.appendf("\"ClientID\": \"%d\",", fClientID);
272 0 : json.appendf("\"OpListID\": \"%d\",", fOpListID);
273 0 : json.appendf("\"ChildID\": \"%d\",", fChildID);
274 0 : skrect_to_json(&json, "Bounds", fBounds);
275 0 : if (fStackTrace.count()) {
276 0 : json.append(",\"Stack\": [");
277 0 : for (int i = 0; i < fStackTrace.count(); i++) {
278 0 : json.appendf("\"%s\"", fStackTrace[i].c_str());
279 0 : if (i < fStackTrace.count() - 1) {
280 0 : json.append(",");
281 : }
282 : }
283 0 : json.append("]");
284 : }
285 0 : json.append("}");
286 0 : return json;
287 : }
288 :
289 0 : SkString GrAuditTrail::OpNode::toJson() const {
290 0 : SkString json;
291 0 : json.append("{");
292 0 : json.appendf("\"ResourceID\": \"%u\",", fResourceUniqueID.asUInt());
293 0 : json.appendf("\"ProxyID\": \"%u\",", fProxyUniqueID.asUInt());
294 0 : skrect_to_json(&json, "Bounds", fBounds);
295 0 : JsonifyTArray(&json, "Ops", fChildren, true);
296 0 : json.append("}");
297 0 : return json;
298 : }
|