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