class GrAuditTrail {
public:
GrAuditTrail()
- : fClientID(kInvalidID)
- , fEnabled(false) {}
+ : fEnabled(false)
+ , fUniqueID(0) {}
+
+ class AutoFrame {
+ public:
+ AutoFrame(GrAuditTrail* auditTrail, const char* name)
+ : fAuditTrail(auditTrail) {
+ if (fAuditTrail->fEnabled) {
+ fAuditTrail->pushFrame(name);
+ }
+ }
+
+ ~AutoFrame() {
+ if (fAuditTrail->fEnabled) {
+ fAuditTrail->popFrame();
+ }
+ }
+
+ private:
+ GrAuditTrail* fAuditTrail;
+ };
class AutoEnable {
public:
GrAuditTrail* fAuditTrail;
};
- class AutoCollectBatches {
- public:
- AutoCollectBatches(GrAuditTrail* auditTrail, int clientID)
- : fAutoEnable(auditTrail)
- , fAuditTrail(auditTrail) {
- fAuditTrail->setClientID(clientID);
+ void pushFrame(const char* name) {
+ SkASSERT(fEnabled);
+ Frame* frame = new Frame;
+ fEvents.emplace_back(frame);
+ if (fStack.empty()) {
+ fFrames.push_back(frame);
+ } else {
+ fStack.back()->fChildren.push_back(frame);
}
- ~AutoCollectBatches() { fAuditTrail->setClientID(kInvalidID); }
+ frame->fUniqueID = fUniqueID++;
+ frame->fName = name;
+ fStack.push_back(frame);
+ }
- private:
- AutoEnable fAutoEnable;
- GrAuditTrail* fAuditTrail;
- };
+ void popFrame() {
+ SkASSERT(fEnabled);
+ fStack.pop_back();
+ }
void addBatch(const char* name, const SkRect& bounds) {
- SkASSERT(fEnabled);
+ SkASSERT(fEnabled && !fStack.empty());
Batch* batch = new Batch;
- fBatchPool.emplace_back(batch);
+ fEvents.emplace_back(batch);
+ fStack.back()->fChildren.push_back(batch);
batch->fName = name;
batch->fBounds = bounds;
- batch->fClientID = kInvalidID;
- batch->fBatchListID = kInvalidID;
- batch->fChildID = kInvalidID;
fCurrentBatch = batch;
-
- if (fClientID != kInvalidID) {
- batch->fClientID = fClientID;
- Batches** batchesLookup = fClientIDLookup.find(fClientID);
- Batches* batches = nullptr;
- if (!batchesLookup) {
- batches = new Batches;
- fClientIDLookup.set(fClientID, batches);
- } else {
- batches = *batchesLookup;
- }
-
- batches->push_back(fCurrentBatch);
- }
}
void batchingResultCombined(GrBatch* combiner);
void batchingResultNew(GrBatch* batch);
- // Because batching is heavily dependent on sequence of draw calls, these calls will only
- // produce valid information for the given draw sequence which preceeded them.
- // Specifically, future draw calls may change the batching and thus would invalidate
- // the json. What this means is that for some sequence of draw calls N, the below toJson
- // calls will only produce JSON which reflects N draw calls. This JSON may or may not be
- // accurate for N + 1 or N - 1 draws depending on the actual batching algorithm used.
- SkString toJson(bool prettyPrint = false) const;
-
- // returns a json string of all of the batches associated with a given client id
- SkString toJson(int clientID, bool prettyPrint = false) const;
+ SkString toJson(bool batchList = false, bool prettyPrint = false) const;
bool isEnabled() { return fEnabled; }
void setEnabled(bool enabled) { fEnabled = enabled; }
- void setClientID(int clientID) { fClientID = clientID; }
+ void reset() {
+ SkASSERT(fEnabled && fStack.empty());
+ fFrames.reset();
+ }
- void fullReset() {
+ void resetBatchList() {
SkASSERT(fEnabled);
- fBatchList.reset();
+ fBatches.reset();
fIDLookup.reset();
- // free all client batches
- fClientIDLookup.foreach([](const int&, Batches** batches) { delete *batches; });
- fClientIDLookup.reset();
- fBatchPool.reset(); // must be last, frees all of the memory
}
- static const int kInvalidID;
+ void fullReset() {
+ SkASSERT(fEnabled);
+ this->reset();
+ this->resetBatchList();
+ fEvents.reset(); // must be last, frees all of the memory
+ }
private:
// TODO if performance becomes an issue, we can move to using SkVarAlloc
- struct Batch {
- SkString toJson() const;
- SkString fName;
- SkRect fBounds;
- int fClientID;
- int fBatchListID;
- int fChildID;
+ struct Event {
+ virtual ~Event() {}
+ virtual SkString toJson() const=0;
+
+ const char* fName;
+ uint64_t fUniqueID;
};
- typedef SkTArray<SkAutoTDelete<Batch>, true> BatchPool;
- typedef SkTArray<Batch*> Batches;
+ struct Frame : public Event {
+ SkString toJson() const override;
+ SkTArray<Event*> fChildren;
+ };
- struct BatchNode {
- SkString toJson() const;
+ struct Batch : public Event {
+ SkString toJson() const override;
SkRect fBounds;
- Batches fChildren;
+ SkTArray<Batch*> fChildren;
};
- typedef SkTArray<SkAutoTDelete<BatchNode>, true> BatchList;
+ typedef SkTArray<SkAutoTDelete<Event>, true> EventArrayPool;
template <typename T>
static void JsonifyTArray(SkString* json, const char* name, const T& array,
bool addComma);
+ // We store both an array of frames, and also a flatter array just of the batches
+ bool fEnabled;
+ EventArrayPool fEvents; // manages the lifetimes of the events
+ SkTArray<Event*> fFrames;
+ SkTArray<Frame*> fStack;
+ uint64_t fUniqueID;
+
Batch* fCurrentBatch;
- BatchPool fBatchPool;
SkTHashMap<GrBatch*, int> fIDLookup;
- SkTHashMap<int, Batches*> fClientIDLookup;
- BatchList fBatchList;
-
- // The client cas pass in an optional client ID which we will use to mark the batches
- int fClientID;
- bool fEnabled;
+ SkTArray<Batch*> fBatches;
};
#define GR_AUDIT_TRAIL_INVOKE_GUARD(audit_trail, invoke, ...) \
}
#define GR_AUDIT_TRAIL_AUTO_FRAME(audit_trail, framename) \
- // TODO fill out the frame stuff
- //GrAuditTrail::AutoFrame SK_MACRO_APPEND_LINE(auto_frame)(audit_trail, framename);
+ GrAuditTrail::AutoFrame SK_MACRO_APPEND_LINE(auto_frame)(audit_trail, framename);
#define GR_AUDIT_TRAIL_RESET(audit_trail) \
- //GR_AUDIT_TRAIL_INVOKE_GUARD(audit_trail, reset);
+ GR_AUDIT_TRAIL_INVOKE_GUARD(audit_trail, reset);
#define GR_AUDIT_TRAIL_ADDBATCH(audit_trail, batchname, bounds) \
GR_AUDIT_TRAIL_INVOKE_GUARD(audit_trail, addBatch, batchname, bounds);
#include "GrAuditTrail.h"
#include "batches/GrBatch.h"
-const int GrAuditTrail::kInvalidID = -1;
-
void GrAuditTrail::batchingResultCombined(GrBatch* combiner) {
int* indexPtr = fIDLookup.find(combiner);
SkASSERT(indexPtr);
int index = *indexPtr;
- SkASSERT(index < fBatchList.count());
- BatchNode& batch = *fBatchList[index];
-
- // set the ids for the child batch
- fCurrentBatch->fBatchListID = index;
- fCurrentBatch->fChildID = batch.fChildren.count();
-
- // Update the bounds and store a pointer to the new batch
+ SkASSERT(index < fBatches.count());
+ Batch& batch = *fBatches[index];
+
+ // if this is our first child, we also push back a copy of the original batch and its
+ // bounds
+ if (batch.fChildren.empty()) {
+ Batch* firstBatch = new Batch;
+ firstBatch->fName = batch.fName;
+ firstBatch->fBounds = batch.fBounds;
+ fEvents.emplace_back(firstBatch);
+ batch.fChildren.push_back(firstBatch);
+ }
batch.fChildren.push_back(fCurrentBatch);
batch.fBounds = combiner->bounds();
}
void GrAuditTrail::batchingResultNew(GrBatch* batch) {
- // Our algorithm doesn't bother to reorder inside of a BatchNode
- // so the ChildID will start at 0
- fCurrentBatch->fBatchListID = fBatchList.count();
- fCurrentBatch->fChildID = 0;
-
- // We use the batch pointer as a key to find the batchnode we are 'glomming' batches onto
- fIDLookup.set(batch, fCurrentBatch->fBatchListID);
- BatchNode* batchNode = new BatchNode;
- batchNode->fBounds = fCurrentBatch->fBounds;
- batchNode->fChildren.push_back(fCurrentBatch);
- fBatchList.emplace_back(batchNode);
+ fIDLookup.set(batch, fBatches.count());
+ fBatches.push_back(fCurrentBatch);
}
template <typename T>
return prettyPrintJson.prettify(json);
}
-SkString GrAuditTrail::toJson(bool prettyPrint) const {
+SkString GrAuditTrail::toJson(bool batchList, bool prettyPrint) const {
SkString json;
json.append("{");
- JsonifyTArray(&json, "Batches", fBatchList, false);
- json.append("}");
-
- if (prettyPrint) {
- return pretty_print_json(json);
+ if (!batchList) {
+ JsonifyTArray(&json, "Stacks", fFrames, false);
} else {
- return json;
- }
-}
-
-SkString GrAuditTrail::toJson(int clientID, bool prettyPrint) const {
- SkString json;
- json.append("{");
- Batches** batches = fClientIDLookup.find(clientID);
- if (batches) {
- JsonifyTArray(&json, "Batches", **batches, false);
+ JsonifyTArray(&json, "Batches", fBatches, false);
}
- json.appendf("}");
+ json.append("}");
if (prettyPrint) {
return pretty_print_json(json);
}
}
-static void skrect_to_json(SkString* json, const char* name, const SkRect& rect) {
- json->appendf("\"%s\": {", name);
- json->appendf("\"Left\": %f,", rect.fLeft);
- json->appendf("\"Right\": %f,", rect.fRight);
- json->appendf("\"Top\": %f,", rect.fTop);
- json->appendf("\"Bottom\": %f", rect.fBottom);
- json->append("}");
-}
-
-SkString GrAuditTrail::Batch::toJson() const {
+SkString GrAuditTrail::Frame::toJson() const {
SkString json;
json.append("{");
- json.appendf("\"Name\": \"%s\",", fName.c_str());
- json.appendf("\"ClientID\": \"%d\",", fClientID);
- json.appendf("\"BatchListID\": \"%d\",", fBatchListID);
- json.appendf("\"ChildID\": \"%d\",", fChildID);
- skrect_to_json(&json, "Bounds", fBounds);
+ json.appendf("\"Name\": \"%s\"", fName);
+ JsonifyTArray(&json, "Frames", fChildren, true);
json.append("}");
return json;
}
-SkString GrAuditTrail::BatchNode::toJson() const {
+SkString GrAuditTrail::Batch::toJson() const {
SkString json;
json.append("{");
- skrect_to_json(&json, "Bounds", fBounds);
- JsonifyTArray(&json, "Batches", fChildren, true);
+ json.appendf("\"Name\": \"%s\",", fName);
+ json.append("\"Bounds\": {");
+ json.appendf("\"Left\": %f,", fBounds.fLeft);
+ json.appendf("\"Right\": %f,", fBounds.fRight);
+ json.appendf("\"Top\": %f,", fBounds.fTop);
+ json.appendf("\"Bottom\": %f", fBounds.fBottom);
+ JsonifyTArray(&json, "Children", fChildren, true);
+ json.append("}");
json.append("}");
return json;
}
result[SKDEBUGCANVAS_ATTRIBUTE_VERSION] = Json::Value(SKDEBUGCANVAS_VERSION);
Json::Value commands = Json::Value(Json::arrayValue);
for (int i = 0; i < this->getSize() && i <= n; i++) {
- commands[i] = this->getDrawCommandAt(i)->drawToAndCollectJSON(canvas, urlDataManager,
- i);
+ commands[i] = this->getDrawCommandAt(i)->drawToAndCollectJSON(canvas, urlDataManager);
}
result[SKDEBUGCANVAS_ATTRIBUTE_COMMANDS] = commands;
return result;
}
Json::Value SkDrawCommand::drawToAndCollectJSON(SkCanvas* canvas,
- UrlDataManager& urlDataManager,
- int opIndex) const {
+ UrlDataManager& urlDataManager) const {
Json::Value result = this->toJSON(urlDataManager);
SkASSERT(canvas);
GrContext* ctx = rt->getContext();
if(ctx) {
GrAuditTrail* at = ctx->getAuditTrail();
- GrAuditTrail::AutoCollectBatches enable(at, opIndex);
+ GrAuditTrail::AutoEnable enable(at);
this->execute(canvas);
// TODO if this is inefficient we could add a method to GrAuditTrail which takes
// a Json::Value and is only compiled in this file
Json::Value parsedFromString;
Json::Reader reader;
- SkDEBUGCODE(bool parsingSuccessful = )reader.parse(at->toJson(opIndex).c_str(),
+ SkDEBUGCODE(bool parsingSuccessful = )reader.parse(at->toJson().c_str(),
parsedFromString);
SkASSERT(parsingSuccessful);
result[SKDEBUGCANVAS_ATTRIBUTE_AUDITTRAIL] = parsedFromString;
+ at->reset();
}
}
#endif
virtual Json::Value toJSON(UrlDataManager& urlDataManager) const;
- Json::Value drawToAndCollectJSON(SkCanvas*, UrlDataManager& urlDataManager,
- int opIndex) const;
+ Json::Value drawToAndCollectJSON(SkCanvas*, UrlDataManager& urlDataManager) const;
/* Converts a JSON representation of a command into a newly-allocated SkDrawCommand object. It
* is the caller's responsibility to delete this object. This method may return null if an error
return true;
}
-GrAuditTrail* Request::getAuditTrail(SkCanvas* canvas) {
- GrAuditTrail* at = nullptr;
-#if SK_SUPPORT_GPU
- GrRenderTarget* rt = canvas->internal_private_accessTopLayerRenderTarget();
- if (rt) {
- GrContext* ctx = rt->getContext();
- if (ctx) {
- at = ctx->getAuditTrail();
- }
- }
-#endif
- return at;
-}
-
-void Request::cleanupAuditTrail(SkCanvas* canvas) {
- GrAuditTrail* at = this->getAuditTrail(canvas);
- if (at) {
- GrAuditTrail::AutoEnable ae(at);
- at->fullReset();
- }
-}
-
SkData* Request::getJsonOps(int n) {
SkCanvas* canvas = this->getCanvas();
Json::Value root = fDebugCanvas->toJSON(fUrlDataManager, n, canvas);
SkDynamicMemoryWStream stream;
stream.writeText(Json::FastWriter().write(root).c_str());
- this->cleanupAuditTrail(canvas);
-
return stream.copyToData();
}
// a Json::Value and is only compiled in this file
Json::Value parsedFromString;
#if SK_SUPPORT_GPU
- GrAuditTrail* at = this->getAuditTrail(canvas);
+ GrRenderTarget* rt = canvas->internal_private_accessTopLayerRenderTarget();
+ SkASSERT(rt);
+ GrContext* ctx = rt->getContext();
+ SkASSERT(ctx);
+ GrAuditTrail* at = ctx->getAuditTrail();
GrAuditTrail::AutoManageBatchList enable(at);
fDebugCanvas->drawTo(canvas, n);
void drawToCanvas(int n);
SkSurface* createCPUSurface();
SkSurface* createGPUSurface();
- GrAuditTrail* getAuditTrail(SkCanvas*);
- void cleanupAuditTrail(SkCanvas*);
SkAutoTUnref<SkPicture> fPicture;
SkAutoTDelete<GrContextFactory> fContextFactory;