diff --git a/include/private/GrAuditTrail.h b/include/private/GrAuditTrail.h index 72708644a5..faf59ab0e4 100644 --- a/include/private/GrAuditTrail.h +++ b/include/private/GrAuditTrail.h @@ -28,27 +28,8 @@ class GrBatch; class GrAuditTrail { public: GrAuditTrail() - : 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; - }; + : fClientID(kInvalidID) + , fEnabled(false) {} class AutoEnable { public: @@ -83,99 +64,113 @@ public: GrAuditTrail* fAuditTrail; }; - 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); + class AutoCollectBatches { + public: + AutoCollectBatches(GrAuditTrail* auditTrail, int clientID) + : fAutoEnable(auditTrail) + , fAuditTrail(auditTrail) { + fAuditTrail->setClientID(clientID); } - frame->fUniqueID = fUniqueID++; - frame->fName = name; - fStack.push_back(frame); - } + ~AutoCollectBatches() { fAuditTrail->setClientID(kInvalidID); } - void popFrame() { - SkASSERT(fEnabled); - fStack.pop_back(); - } + private: + AutoEnable fAutoEnable; + GrAuditTrail* fAuditTrail; + }; void addBatch(const char* name, const SkRect& bounds) { - SkASSERT(fEnabled && !fStack.empty()); + SkASSERT(fEnabled); Batch* batch = new Batch; - fEvents.emplace_back(batch); - fStack.back()->fChildren.push_back(batch); + fBatchPool.emplace_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); - SkString toJson(bool batchList = false, bool prettyPrint = false) const; + // 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; bool isEnabled() { return fEnabled; } void setEnabled(bool enabled) { fEnabled = enabled; } - void reset() { - SkASSERT(fEnabled && fStack.empty()); - fFrames.reset(); - } - - void resetBatchList() { - SkASSERT(fEnabled); - fBatches.reset(); - fIDLookup.reset(); - } + void setClientID(int clientID) { fClientID = clientID; } void fullReset() { SkASSERT(fEnabled); - this->reset(); - this->resetBatchList(); - fEvents.reset(); // must be last, frees all of the memory + fBatchList.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; + private: // TODO if performance becomes an issue, we can move to using SkVarAlloc - struct Event { - virtual ~Event() {} - virtual SkString toJson() const=0; - - const char* fName; - uint64_t fUniqueID; - }; - - struct Frame : public Event { - SkString toJson() const override; - SkTArray fChildren; - }; - - struct Batch : public Event { - SkString toJson() const override; + struct Batch { + SkString toJson() const; + SkString fName; SkRect fBounds; - SkTArray fChildren; + int fClientID; + int fBatchListID; + int fChildID; }; - typedef SkTArray, true> EventArrayPool; + typedef SkTArray, true> BatchPool; + + typedef SkTArray Batches; + + struct BatchNode { + SkString toJson() const; + SkRect fBounds; + Batches fChildren; + }; + typedef SkTArray, true> BatchList; template 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 fFrames; - SkTArray fStack; - uint64_t fUniqueID; - Batch* fCurrentBatch; + BatchPool fBatchPool; SkTHashMap fIDLookup; - SkTArray fBatches; + SkTHashMap fClientIDLookup; + BatchList fBatchList; + + // The client cas pass in an optional client ID which we will use to mark the batches + int fClientID; + bool fEnabled; }; #define GR_AUDIT_TRAIL_INVOKE_GUARD(audit_trail, invoke, ...) \ @@ -184,10 +179,11 @@ private: } #define GR_AUDIT_TRAIL_AUTO_FRAME(audit_trail, framename) \ - GrAuditTrail::AutoFrame SK_MACRO_APPEND_LINE(auto_frame)(audit_trail, framename); + // TODO fill out the frame stuff + //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); diff --git a/src/gpu/GrAuditTrail.cpp b/src/gpu/GrAuditTrail.cpp index 59201f4450..dae50221a2 100644 --- a/src/gpu/GrAuditTrail.cpp +++ b/src/gpu/GrAuditTrail.cpp @@ -8,29 +8,36 @@ #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 < fBatches.count()); - Batch& batch = *fBatches[index]; + SkASSERT(index < fBatchList.count()); + BatchNode& batch = *fBatchList[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); - } + // 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 batch.fChildren.push_back(fCurrentBatch); batch.fBounds = combiner->bounds(); } void GrAuditTrail::batchingResultNew(GrBatch* batch) { - fIDLookup.set(batch, fBatches.count()); - fBatches.push_back(fCurrentBatch); + // 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); } template @@ -118,14 +125,10 @@ static SkString pretty_print_json(SkString json) { return prettyPrintJson.prettify(json); } -SkString GrAuditTrail::toJson(bool batchList, bool prettyPrint) const { +SkString GrAuditTrail::toJson(bool prettyPrint) const { SkString json; json.append("{"); - if (!batchList) { - JsonifyTArray(&json, "Stacks", fFrames, false); - } else { - JsonifyTArray(&json, "Batches", fBatches, false); - } + JsonifyTArray(&json, "Batches", fBatchList, false); json.append("}"); if (prettyPrint) { @@ -135,26 +138,48 @@ SkString GrAuditTrail::toJson(bool batchList, bool prettyPrint) const { } } -SkString GrAuditTrail::Frame::toJson() const { +SkString GrAuditTrail::toJson(int clientID, bool prettyPrint) const { SkString json; json.append("{"); - json.appendf("\"Name\": \"%s\"", fName); - JsonifyTArray(&json, "Frames", fChildren, true); - json.append("}"); - return json; + Batches** batches = fClientIDLookup.find(clientID); + if (batches) { + JsonifyTArray(&json, "Batches", **batches, false); + } + json.appendf("}"); + + if (prettyPrint) { + return pretty_print_json(json); + } else { + return 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 json; json.append("{"); - 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.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.append("}"); + return json; +} + +SkString GrAuditTrail::BatchNode::toJson() const { + SkString json; + json.append("{"); + skrect_to_json(&json, "Bounds", fBounds); + JsonifyTArray(&json, "Batches", fChildren, true); json.append("}"); return json; } diff --git a/tools/debugger/SkDebugCanvas.cpp b/tools/debugger/SkDebugCanvas.cpp index 34e582e187..cc1733b705 100644 --- a/tools/debugger/SkDebugCanvas.cpp +++ b/tools/debugger/SkDebugCanvas.cpp @@ -336,7 +336,8 @@ Json::Value SkDebugCanvas::toJSON(UrlDataManager& urlDataManager, int n, SkCanva 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); + commands[i] = this->getDrawCommandAt(i)->drawToAndCollectJSON(canvas, urlDataManager, + i); } result[SKDEBUGCANVAS_ATTRIBUTE_COMMANDS] = commands; return result; diff --git a/tools/debugger/SkDrawCommand.cpp b/tools/debugger/SkDrawCommand.cpp index 33e49ce75e..1af299831f 100644 --- a/tools/debugger/SkDrawCommand.cpp +++ b/tools/debugger/SkDrawCommand.cpp @@ -228,7 +228,8 @@ Json::Value SkDrawCommand::toJSON(UrlDataManager& urlDataManager) const { } Json::Value SkDrawCommand::drawToAndCollectJSON(SkCanvas* canvas, - UrlDataManager& urlDataManager) const { + UrlDataManager& urlDataManager, + int opIndex) const { Json::Value result = this->toJSON(urlDataManager); SkASSERT(canvas); @@ -239,19 +240,18 @@ Json::Value SkDrawCommand::drawToAndCollectJSON(SkCanvas* canvas, GrContext* ctx = rt->getContext(); if(ctx) { GrAuditTrail* at = ctx->getAuditTrail(); - GrAuditTrail::AutoEnable enable(at); + GrAuditTrail::AutoCollectBatches enable(at, opIndex); 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().c_str(), + SkDEBUGCODE(bool parsingSuccessful = )reader.parse(at->toJson(opIndex).c_str(), parsedFromString); SkASSERT(parsingSuccessful); result[SKDEBUGCANVAS_ATTRIBUTE_AUDITTRAIL] = parsedFromString; - at->reset(); } } #endif diff --git a/tools/debugger/SkDrawCommand.h b/tools/debugger/SkDrawCommand.h index 34832a7cf1..74c2b2c69e 100644 --- a/tools/debugger/SkDrawCommand.h +++ b/tools/debugger/SkDrawCommand.h @@ -102,7 +102,8 @@ public: virtual Json::Value toJSON(UrlDataManager& urlDataManager) const; - Json::Value drawToAndCollectJSON(SkCanvas*, UrlDataManager& urlDataManager) const; + Json::Value drawToAndCollectJSON(SkCanvas*, UrlDataManager& urlDataManager, + int opIndex) 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 diff --git a/tools/skiaserve/Request.cpp b/tools/skiaserve/Request.cpp index 4d256d56d8..bcaebde01f 100644 --- a/tools/skiaserve/Request.cpp +++ b/tools/skiaserve/Request.cpp @@ -138,6 +138,28 @@ bool Request::enableGPU(bool enable) { 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); @@ -145,6 +167,8 @@ SkData* Request::getJsonOps(int n) { SkDynamicMemoryWStream stream; stream.writeText(Json::FastWriter().write(root).c_str()); + this->cleanupAuditTrail(canvas); + return stream.copyToData(); } @@ -156,11 +180,7 @@ SkData* Request::getJsonBatchList(int n) { // a Json::Value and is only compiled in this file Json::Value parsedFromString; #if SK_SUPPORT_GPU - GrRenderTarget* rt = canvas->internal_private_accessTopLayerRenderTarget(); - SkASSERT(rt); - GrContext* ctx = rt->getContext(); - SkASSERT(ctx); - GrAuditTrail* at = ctx->getAuditTrail(); + GrAuditTrail* at = this->getAuditTrail(canvas); GrAuditTrail::AutoManageBatchList enable(at); fDebugCanvas->drawTo(canvas, n); diff --git a/tools/skiaserve/Request.h b/tools/skiaserve/Request.h index 568518644c..bef8c4a810 100644 --- a/tools/skiaserve/Request.h +++ b/tools/skiaserve/Request.h @@ -59,6 +59,8 @@ struct Request { private: SkSurface* createCPUSurface(); SkSurface* createGPUSurface(); + GrAuditTrail* getAuditTrail(SkCanvas*); + void cleanupAuditTrail(SkCanvas*); SkAutoTDelete fContextFactory; SkAutoTUnref fSurface;