Add abilitly to query audit trail for batches by draw op
BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1745513002 Review URL: https://codereview.chromium.org/1745513002
This commit is contained in:
parent
7f4b1b20b0
commit
9b48a6e3f8
@ -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<Event*> fChildren;
|
||||
};
|
||||
|
||||
struct Batch : public Event {
|
||||
SkString toJson() const override;
|
||||
struct Batch {
|
||||
SkString toJson() const;
|
||||
SkString fName;
|
||||
SkRect fBounds;
|
||||
SkTArray<Batch*> fChildren;
|
||||
int fClientID;
|
||||
int fBatchListID;
|
||||
int fChildID;
|
||||
};
|
||||
typedef SkTArray<SkAutoTDelete<Event>, true> EventArrayPool;
|
||||
typedef SkTArray<SkAutoTDelete<Batch>, true> BatchPool;
|
||||
|
||||
typedef SkTArray<Batch*> Batches;
|
||||
|
||||
struct BatchNode {
|
||||
SkString toJson() const;
|
||||
SkRect fBounds;
|
||||
Batches fChildren;
|
||||
};
|
||||
typedef SkTArray<SkAutoTDelete<BatchNode>, true> BatchList;
|
||||
|
||||
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;
|
||||
SkTArray<Batch*> fBatches;
|
||||
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;
|
||||
};
|
||||
|
||||
#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);
|
||||
|
@ -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 <typename T>
|
||||
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -59,6 +59,8 @@ struct Request {
|
||||
private:
|
||||
SkSurface* createCPUSurface();
|
||||
SkSurface* createGPUSurface();
|
||||
GrAuditTrail* getAuditTrail(SkCanvas*);
|
||||
void cleanupAuditTrail(SkCanvas*);
|
||||
|
||||
SkAutoTDelete<GrContextFactory> fContextFactory;
|
||||
SkAutoTUnref<SkSurface> fSurface;
|
||||
|
Loading…
Reference in New Issue
Block a user