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 #include "SkJSONWriter.h" 11 12 const int GrAuditTrail::kGrAuditTrailInvalidID = -1; 13 14 void GrAuditTrail::addOp(const GrOp* op, GrRenderTargetProxy::UniqueID proxyID) { 15 SkASSERT(fEnabled); 16 Op* auditOp = new Op; 17 fOpPool.emplace_back(auditOp); 18 auditOp->fName = op->name(); 19 auditOp->fBounds = op->bounds(); 20 auditOp->fClientID = kGrAuditTrailInvalidID; 21 auditOp->fOpListID = kGrAuditTrailInvalidID; 22 auditOp->fChildID = kGrAuditTrailInvalidID; 23 24 // consume the current stack trace if any 25 auditOp->fStackTrace = fCurrentStackTrace; 26 fCurrentStackTrace.reset(); 27 28 if (fClientID != kGrAuditTrailInvalidID) { 29 auditOp->fClientID = fClientID; 30 Ops** opsLookup = fClientIDLookup.find(fClientID); 31 Ops* ops = nullptr; 32 if (!opsLookup) { 33 ops = new Ops; 34 fClientIDLookup.set(fClientID, ops); 35 } else { 36 ops = *opsLookup; 37 } 38 39 ops->push_back(auditOp); 40 } 41 42 // Our algorithm doesn't bother to reorder inside of an OpNode so the ChildID will start at 0 43 auditOp->fOpListID = fOpList.count(); 44 auditOp->fChildID = 0; 45 46 // We use the op pointer as a key to find the OpNode we are 'glomming' ops onto 47 fIDLookup.set(op->uniqueID(), auditOp->fOpListID); 48 OpNode* opNode = new OpNode(proxyID); 49 opNode->fBounds = op->bounds(); 50 opNode->fChildren.push_back(auditOp); 51 fOpList.emplace_back(opNode); 52 } 53 54 void GrAuditTrail::opsCombined(const GrOp* consumer, const GrOp* consumed) { 55 // Look up the op we are going to glom onto 56 int* indexPtr = fIDLookup.find(consumer->uniqueID()); 57 SkASSERT(indexPtr); 58 int index = *indexPtr; 59 SkASSERT(index < fOpList.count() && fOpList[index]); 60 OpNode& consumerOp = *fOpList[index]; 61 62 // Look up the op which will be glommed 63 int* consumedPtr = fIDLookup.find(consumed->uniqueID()); 64 SkASSERT(consumedPtr); 65 int consumedIndex = *consumedPtr; 66 SkASSERT(consumedIndex < fOpList.count() && fOpList[consumedIndex]); 67 OpNode& consumedOp = *fOpList[consumedIndex]; 68 69 // steal all of consumed's ops 70 for (int i = 0; i < consumedOp.fChildren.count(); i++) { 71 Op* childOp = consumedOp.fChildren[i]; 72 73 // set the ids for the child op 74 childOp->fOpListID = index; 75 childOp->fChildID = consumerOp.fChildren.count(); 76 consumerOp.fChildren.push_back(childOp); 77 } 78 79 // Update the bounds for the combineWith node 80 consumerOp.fBounds = consumer->bounds(); 81 82 // remove the old node from our opList and clear the combinee's lookup 83 // NOTE: because we can't change the shape of the oplist, we use a sentinel 84 fOpList[consumedIndex].reset(nullptr); 85 fIDLookup.remove(consumed->uniqueID()); 86 } 87 88 void GrAuditTrail::copyOutFromOpList(OpInfo* outOpInfo, int opListID) { 89 SkASSERT(opListID < fOpList.count()); 90 const OpNode* bn = fOpList[opListID].get(); 91 SkASSERT(bn); 92 outOpInfo->fBounds = bn->fBounds; 93 outOpInfo->fProxyUniqueID = bn->fProxyUniqueID; 94 for (int j = 0; j < bn->fChildren.count(); j++) { 95 OpInfo::Op& outOp = outOpInfo->fOps.push_back(); 96 const Op* currentOp = bn->fChildren[j]; 97 outOp.fBounds = currentOp->fBounds; 98 outOp.fClientID = currentOp->fClientID; 99 } 100 } 101 102 void GrAuditTrail::getBoundsByClientID(SkTArray<OpInfo>* outInfo, int clientID) { 103 Ops** opsLookup = fClientIDLookup.find(clientID); 104 if (opsLookup) { 105 // We track which oplistID we're currently looking at. If it changes, then we need to push 106 // back a new op info struct. We happen to know that ops are in sequential order in the 107 // oplist, otherwise we'd have to do more bookkeeping 108 int currentOpListID = kGrAuditTrailInvalidID; 109 for (int i = 0; i < (*opsLookup)->count(); i++) { 110 const Op* op = (**opsLookup)[i]; 111 112 // Because we will copy out all of the ops associated with a given op list id everytime 113 // the id changes, we only have to update our struct when the id changes. 114 if (kGrAuditTrailInvalidID == currentOpListID || op->fOpListID != currentOpListID) { 115 OpInfo& outOpInfo = outInfo->push_back(); 116 117 // copy out all of the ops so the client can display them even if they have a 118 // different clientID 119 this->copyOutFromOpList(&outOpInfo, op->fOpListID); 120 } 121 } 122 } 123 } 124 125 void GrAuditTrail::getBoundsByOpListID(OpInfo* outInfo, int opListID) { 126 this->copyOutFromOpList(outInfo, opListID); 127 } 128 129 void GrAuditTrail::fullReset() { 130 SkASSERT(fEnabled); 131 fOpList.reset(); 132 fIDLookup.reset(); 133 // free all client ops 134 fClientIDLookup.foreach ([](const int&, Ops** ops) { delete *ops; }); 135 fClientIDLookup.reset(); 136 fOpPool.reset(); // must be last, frees all of the memory 137 } 138 139 template <typename T> 140 void GrAuditTrail::JsonifyTArray(SkJSONWriter& writer, const char* name, const T& array) { 141 if (array.count()) { 142 writer.beginArray(name); 143 for (int i = 0; i < array.count(); i++) { 144 // Handle sentinel nullptrs 145 if (array[i]) { 146 array[i]->toJson(writer); 147 } 148 } 149 writer.endArray(); 150 } 151 } 152 153 void GrAuditTrail::toJson(SkJSONWriter& writer) const { 154 writer.beginObject(); 155 JsonifyTArray(writer, "Ops", fOpList); 156 writer.endObject(); 157 } 158 159 void GrAuditTrail::toJson(SkJSONWriter& writer, int clientID) const { 160 writer.beginObject(); 161 Ops** ops = fClientIDLookup.find(clientID); 162 if (ops) { 163 JsonifyTArray(writer, "Ops", **ops); 164 } 165 writer.endObject(); 166 } 167 168 static void skrect_to_json(SkJSONWriter& writer, const char* name, const SkRect& rect) { 169 writer.beginObject(name); 170 writer.appendFloat("Left", rect.fLeft); 171 writer.appendFloat("Right", rect.fRight); 172 writer.appendFloat("Top", rect.fTop); 173 writer.appendFloat("Bottom", rect.fBottom); 174 writer.endObject(); 175 } 176 177 void GrAuditTrail::Op::toJson(SkJSONWriter& writer) const { 178 writer.beginObject(); 179 writer.appendString("Name", fName.c_str()); 180 writer.appendS32("ClientID", fClientID); 181 writer.appendS32("OpListID", fOpListID); 182 writer.appendS32("ChildID", fChildID); 183 skrect_to_json(writer, "Bounds", fBounds); 184 if (fStackTrace.count()) { 185 writer.beginArray("Stack"); 186 for (int i = 0; i < fStackTrace.count(); i++) { 187 writer.appendString(fStackTrace[i].c_str()); 188 } 189 writer.endArray(); 190 } 191 writer.endObject(); 192 } 193 194 void GrAuditTrail::OpNode::toJson(SkJSONWriter& writer) const { 195 writer.beginObject(); 196 writer.appendU32("ProxyID", fProxyUniqueID.asUInt()); 197 skrect_to_json(writer, "Bounds", fBounds); 198 JsonifyTArray(writer, "Ops", fChildren); 199 writer.endObject(); 200 } 201