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 "GrGpuResource.h"
13#include "GrRenderTargetProxy.h"
14#include "SkRect.h"
15#include "SkString.h"
16#include "SkTArray.h"
17#include "SkTHash.h"
18
19class GrOp;
20
21/*
22 * GrAuditTrail collects a list of draw ops, detailed information about those ops, and can dump them
23 * to json.
24 *
25 * Capturing this information is expensive and consumes a lot of memory, therefore it is important
26 * to enable auditing only when required and disable it promptly. The AutoEnable class helps to
27 * ensure that the audit trail is disabled in a timely fashion. Once the information has been dealt
28 * with, be sure to call reset(), or the log will simply keep growing.
29 */
30class GrAuditTrail {
31public:
32    GrAuditTrail()
33    : fClientID(kGrAuditTrailInvalidID)
34    , fEnabled(false) {}
35
36    class AutoEnable {
37    public:
38        AutoEnable(GrAuditTrail* auditTrail)
39            : fAuditTrail(auditTrail) {
40            SkASSERT(!fAuditTrail->isEnabled());
41            fAuditTrail->setEnabled(true);
42        }
43
44        ~AutoEnable() {
45            SkASSERT(fAuditTrail->isEnabled());
46            fAuditTrail->setEnabled(false);
47        }
48
49    private:
50        GrAuditTrail* fAuditTrail;
51    };
52
53    class AutoManageOpList {
54    public:
55        AutoManageOpList(GrAuditTrail* auditTrail)
56                : fAutoEnable(auditTrail), fAuditTrail(auditTrail) {}
57
58        ~AutoManageOpList() { fAuditTrail->fullReset(); }
59
60    private:
61        AutoEnable fAutoEnable;
62        GrAuditTrail* fAuditTrail;
63    };
64
65    class AutoCollectOps {
66    public:
67        AutoCollectOps(GrAuditTrail* auditTrail, int clientID)
68                : fAutoEnable(auditTrail), fAuditTrail(auditTrail) {
69            fAuditTrail->setClientID(clientID);
70        }
71
72        ~AutoCollectOps() { fAuditTrail->setClientID(kGrAuditTrailInvalidID); }
73
74    private:
75        AutoEnable fAutoEnable;
76        GrAuditTrail* fAuditTrail;
77    };
78
79    void pushFrame(const char* framename) {
80        SkASSERT(fEnabled);
81        fCurrentStackTrace.push_back(SkString(framename));
82    }
83
84    void addOp(const GrOp*, GrRenderTargetProxy::UniqueID proxyID);
85
86    void opsCombined(const GrOp* consumer, const GrOp* consumed);
87
88    // Because op combining is heavily dependent on sequence of draw calls, these calls will only
89    // produce valid information for the given draw sequence which preceeded them. Specifically, ops
90    // of future draw calls may combine with previous ops and thus would invalidate the json. What
91    // this means is that for some sequence of draw calls N, the below toJson calls will only
92    // produce JSON which reflects N draw calls. This JSON may or may not be accurate for N + 1 or
93    // N - 1 draws depending on the actual combining algorithm used.
94    SkString toJson(bool prettyPrint = false) const;
95
96    // returns a json string of all of the ops associated with a given client id
97    SkString toJson(int clientID, bool prettyPrint = false) const;
98
99    bool isEnabled() { return fEnabled; }
100    void setEnabled(bool enabled) { fEnabled = enabled; }
101
102    void setClientID(int clientID) { fClientID = clientID; }
103
104    // We could just return our internal bookkeeping struct if copying the data out becomes
105    // a performance issue, but until then its nice to decouple
106    struct OpInfo {
107        struct Op {
108            int    fClientID;
109            SkRect fBounds;
110        };
111
112        SkRect                   fBounds;
113        GrSurfaceProxy::UniqueID fProxyUniqueID;
114        SkTArray<Op>             fOps;
115    };
116
117    void getBoundsByClientID(SkTArray<OpInfo>* outInfo, int clientID);
118    void getBoundsByOpListID(OpInfo* outInfo, int opListID);
119
120    void fullReset();
121
122    static const int kGrAuditTrailInvalidID;
123
124private:
125    // TODO if performance becomes an issue, we can move to using SkVarAlloc
126    struct Op {
127        SkString toJson() const;
128        SkString fName;
129        SkTArray<SkString> fStackTrace;
130        SkRect fBounds;
131        int fClientID;
132        int fOpListID;
133        int fChildID;
134    };
135    typedef SkTArray<std::unique_ptr<Op>, true> OpPool;
136
137    typedef SkTArray<Op*> Ops;
138
139    struct OpNode {
140        OpNode(const GrSurfaceProxy::UniqueID& proxyID) : fProxyUniqueID(proxyID) { }
141        SkString toJson() const;
142
143        SkRect                         fBounds;
144        Ops                            fChildren;
145        const GrSurfaceProxy::UniqueID fProxyUniqueID;
146    };
147    typedef SkTArray<std::unique_ptr<OpNode>, true> OpList;
148
149    void copyOutFromOpList(OpInfo* outOpInfo, int opListID);
150
151    template <typename T>
152    static void JsonifyTArray(SkString* json, const char* name, const T& array,
153                              bool addComma);
154
155    OpPool fOpPool;
156    SkTHashMap<uint32_t, int> fIDLookup;
157    SkTHashMap<int, Ops*> fClientIDLookup;
158    OpList fOpList;
159    SkTArray<SkString> fCurrentStackTrace;
160
161    // The client can pass in an optional client ID which we will use to mark the ops
162    int fClientID;
163    bool fEnabled;
164};
165
166#define GR_AUDIT_TRAIL_INVOKE_GUARD(audit_trail, invoke, ...) \
167    if (audit_trail->isEnabled()) {                           \
168        audit_trail->invoke(__VA_ARGS__);                     \
169    }
170
171#define GR_AUDIT_TRAIL_AUTO_FRAME(audit_trail, framename) \
172    GR_AUDIT_TRAIL_INVOKE_GUARD((audit_trail), pushFrame, framename);
173
174#define GR_AUDIT_TRAIL_RESET(audit_trail) \
175    //GR_AUDIT_TRAIL_INVOKE_GUARD(audit_trail, fullReset);
176
177#define GR_AUDIT_TRAIL_ADD_OP(audit_trail, op, proxy_id) \
178    GR_AUDIT_TRAIL_INVOKE_GUARD(audit_trail, addOp, op, proxy_id);
179
180#define GR_AUDIT_TRAIL_OPS_RESULT_COMBINED(audit_trail, combineWith, op) \
181    GR_AUDIT_TRAIL_INVOKE_GUARD(audit_trail, opsCombined, combineWith, op);
182
183#define GR_AUDIT_TRAIL_OP_RESULT_NEW(audit_trail, op) // Doesn't do anything now, one day...
184
185#endif
186