aboutsummaryrefslogtreecommitdiffhomepage
path: root/include/private/GrAuditTrail.h
blob: c6175501e1a0ec47b147918cdf9c4704651a47b3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
/*
 * Copyright 2016 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#ifndef GrAuditTrail_DEFINED
#define GrAuditTrail_DEFINED

#include "GrConfig.h"
#include "GrGpuResource.h"
#include "GrRenderTargetProxy.h"
#include "SkRect.h"
#include "SkString.h"
#include "SkTArray.h"
#include "SkTHash.h"

class GrOp;

/*
 * GrAuditTrail collects a list of draw ops, detailed information about those ops, and can dump them
 * to json.
 *
 * Capturing this information is expensive and consumes a lot of memory, therefore it is important
 * to enable auditing only when required and disable it promptly. The AutoEnable class helps to
 * ensure that the audit trail is disabled in a timely fashion. Once the information has been dealt
 * with, be sure to call reset(), or the log will simply keep growing.
 */
class GrAuditTrail {
public:
    GrAuditTrail()
    : fClientID(kGrAuditTrailInvalidID)
    , fEnabled(false) {}

    class AutoEnable {
    public:
        AutoEnable(GrAuditTrail* auditTrail)
            : fAuditTrail(auditTrail) {
            SkASSERT(!fAuditTrail->isEnabled());
            fAuditTrail->setEnabled(true);
        }

        ~AutoEnable() {
            SkASSERT(fAuditTrail->isEnabled());
            fAuditTrail->setEnabled(false);
        }

    private:
        GrAuditTrail* fAuditTrail;
    };

    class AutoManageOpList {
    public:
        AutoManageOpList(GrAuditTrail* auditTrail)
                : fAutoEnable(auditTrail), fAuditTrail(auditTrail) {}

        ~AutoManageOpList() { fAuditTrail->fullReset(); }

    private:
        AutoEnable fAutoEnable;
        GrAuditTrail* fAuditTrail;
    };

    class AutoCollectOps {
    public:
        AutoCollectOps(GrAuditTrail* auditTrail, int clientID)
                : fAutoEnable(auditTrail), fAuditTrail(auditTrail) {
            fAuditTrail->setClientID(clientID);
        }

        ~AutoCollectOps() { fAuditTrail->setClientID(kGrAuditTrailInvalidID); }

    private:
        AutoEnable fAutoEnable;
        GrAuditTrail* fAuditTrail;
    };

    void pushFrame(const char* framename) {
        SkASSERT(fEnabled);
        fCurrentStackTrace.push_back(SkString(framename));
    }

    void addOp(const GrOp*, GrRenderTargetProxy::UniqueID proxyID);

    void opsCombined(const GrOp* consumer, const GrOp* consumed);

    // Because op combining is heavily dependent on sequence of draw calls, these calls will only
    // produce valid information for the given draw sequence which preceeded them. Specifically, ops
    // of future draw calls may combine with previous ops 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 combining algorithm used.
    SkString toJson(bool prettyPrint = false) const;

    // returns a json string of all of the ops 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 setClientID(int clientID) { fClientID = clientID; }

    // We could just return our internal bookkeeping struct if copying the data out becomes
    // a performance issue, but until then its nice to decouple
    struct OpInfo {
        struct Op {
            int    fClientID;
            SkRect fBounds;
        };

        SkRect                   fBounds;
        GrSurfaceProxy::UniqueID fProxyUniqueID;
        SkTArray<Op>             fOps;
    };

    void getBoundsByClientID(SkTArray<OpInfo>* outInfo, int clientID);
    void getBoundsByOpListID(OpInfo* outInfo, int opListID);

    void fullReset();

    static const int kGrAuditTrailInvalidID;

private:
    // TODO if performance becomes an issue, we can move to using SkVarAlloc
    struct Op {
        SkString toJson() const;
        SkString fName;
        SkTArray<SkString> fStackTrace;
        SkRect fBounds;
        int fClientID;
        int fOpListID;
        int fChildID;
    };
    typedef SkTArray<std::unique_ptr<Op>, true> OpPool;

    typedef SkTArray<Op*> Ops;

    struct OpNode {
        OpNode(const GrSurfaceProxy::UniqueID& proxyID) : fProxyUniqueID(proxyID) { }
        SkString toJson() const;

        SkRect                         fBounds;
        Ops                            fChildren;
        const GrSurfaceProxy::UniqueID fProxyUniqueID;
    };
    typedef SkTArray<std::unique_ptr<OpNode>, true> OpList;

    void copyOutFromOpList(OpInfo* outOpInfo, int opListID);

    template <typename T>
    static void JsonifyTArray(SkString* json, const char* name, const T& array,
                              bool addComma);

    OpPool fOpPool;
    SkTHashMap<uint32_t, int> fIDLookup;
    SkTHashMap<int, Ops*> fClientIDLookup;
    OpList fOpList;
    SkTArray<SkString> fCurrentStackTrace;

    // The client can pass in an optional client ID which we will use to mark the ops
    int fClientID;
    bool fEnabled;
};

#define GR_AUDIT_TRAIL_INVOKE_GUARD(audit_trail, invoke, ...) \
    if (audit_trail->isEnabled()) {                           \
        audit_trail->invoke(__VA_ARGS__);                     \
    }

#define GR_AUDIT_TRAIL_AUTO_FRAME(audit_trail, framename) \
    GR_AUDIT_TRAIL_INVOKE_GUARD((audit_trail), pushFrame, framename);

#define GR_AUDIT_TRAIL_RESET(audit_trail) \
    //GR_AUDIT_TRAIL_INVOKE_GUARD(audit_trail, fullReset);

#define GR_AUDIT_TRAIL_ADD_OP(audit_trail, op, proxy_id) \
    GR_AUDIT_TRAIL_INVOKE_GUARD(audit_trail, addOp, op, proxy_id);

#define GR_AUDIT_TRAIL_OPS_RESULT_COMBINED(audit_trail, combineWith, op) \
    GR_AUDIT_TRAIL_INVOKE_GUARD(audit_trail, opsCombined, combineWith, op);

#define GR_AUDIT_TRAIL_OP_RESULT_NEW(audit_trail, op) // Doesn't do anything now, one day...

#endif