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