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#ifndef GrAuditTrail_DEFINED
9#define GrAuditTrail_DEFINED
10
11#include "GrConfig.h"
12#include "SkRect.h"
13#include "SkString.h"
14#include "SkTArray.h"
15
16/*
17 * GrAuditTrail collects a list of draw ops, detailed information about those ops, and can dump them
18 * to json.
19 *
20 * Capturing this information is expensive and consumes a lot of memory, therefore it is important
21 * to enable auditing only when required and disable it promptly. The AutoEnable class helps to
22 * ensure that the audit trail is disabled in a timely fashion. Once the information has been dealt
23 * with, be sure to call reset(), or the log will simply keep growing.
24 */
25class GrAuditTrail {
26public:
27    GrAuditTrail()
28    : fEnabled(false)
29    , fUniqueID(0) {}
30
31    class AutoFrame {
32    public:
33        AutoFrame(GrAuditTrail* auditTrail, const char* name)
34            : fAuditTrail(auditTrail) {
35            if (fAuditTrail->fEnabled) {
36                fAuditTrail->pushFrame(name);
37            }
38        }
39
40        ~AutoFrame() {
41            if (fAuditTrail->fEnabled) {
42                fAuditTrail->popFrame();
43            }
44        }
45
46    private:
47        GrAuditTrail* fAuditTrail;
48    };
49
50    class AutoEnable {
51    public:
52        AutoEnable(GrAuditTrail* auditTrail)
53            : fAuditTrail(auditTrail) {
54            SkASSERT(!fAuditTrail->isEnabled());
55            fAuditTrail->setEnabled(true);
56        }
57
58        ~AutoEnable() {
59            SkASSERT(fAuditTrail->isEnabled());
60            fAuditTrail->setEnabled(false);
61        }
62
63    private:
64        GrAuditTrail* fAuditTrail;
65    };
66
67    void pushFrame(const char* name) {
68        SkASSERT(fEnabled);
69        Frame* frame = new Frame;
70        if (fStack.empty()) {
71            fFrames.emplace_back(frame);
72        } else {
73            fStack.back()->fChildren.emplace_back(frame);
74        }
75
76        frame->fUniqueID = fUniqueID++;
77        frame->fName = name;
78        fStack.push_back(frame);
79    }
80
81    void popFrame() {
82        SkASSERT(fEnabled);
83        fStack.pop_back();
84    }
85
86    void addBatch(const char* name, const SkRect& bounds) {
87        SkASSERT(fEnabled && !fStack.empty());
88        Batch* batch = new Batch;
89        fStack.back()->fChildren.emplace_back(batch);
90        batch->fName = name;
91        batch->fBounds = bounds;
92    }
93
94    SkString toJson(bool prettyPrint = false) const;
95
96    bool isEnabled() { return fEnabled; }
97    void setEnabled(bool enabled) { fEnabled = enabled; }
98
99    void reset() { SkASSERT(fEnabled && fStack.empty()); fFrames.reset(); }
100
101private:
102    // TODO if performance becomes an issue, we can move to using SkVarAlloc
103    struct Event {
104        virtual ~Event() {}
105        virtual SkString toJson() const=0;
106
107        const char* fName;
108        uint64_t fUniqueID;
109    };
110
111    typedef SkTArray<SkAutoTDelete<Event>, true> FrameArray;
112    struct Frame : public Event {
113        SkString toJson() const override;
114        FrameArray fChildren;
115    };
116
117    struct Batch : public Event {
118        SkString toJson() const override;
119        SkRect fBounds;
120    };
121
122    static void JsonifyTArray(SkString* json, const char* name, const FrameArray& array,
123                              bool addComma);
124
125    bool fEnabled;
126    FrameArray fFrames;
127    SkTArray<Frame*> fStack;
128    uint64_t fUniqueID;
129};
130
131#define GR_AUDIT_TRAIL_INVOKE_GUARD(audit_trail, invoke, ...) \
132    if (audit_trail->isEnabled()) {                           \
133        audit_trail->invoke(__VA_ARGS__);                     \
134    }
135
136#define GR_AUDIT_TRAIL_AUTO_FRAME(audit_trail, framename) \
137    GrAuditTrail::AutoFrame SK_MACRO_APPEND_LINE(auto_frame)(audit_trail, framename);
138
139#define GR_AUDIT_TRAIL_RESET(audit_trail) \
140    GR_AUDIT_TRAIL_INVOKE_GUARD(audit_trail, reset);
141
142#define GR_AUDIT_TRAIL_ADDBATCH(audit_trail, batchname, bounds) \
143    GR_AUDIT_TRAIL_INVOKE_GUARD(audit_trail, addBatch, batchname, bounds);
144
145#endif
146