SkRecord.h revision 29b1afc169576cf5e708e46b74313b5666e66249
1/*
2 * Copyright 2014 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 SkRecord_DEFINED
9#define SkRecord_DEFINED
10
11#include "SkRecords.h"
12#include "SkTLogic.h"
13#include "SkTemplates.h"
14#include "SkVarAlloc.h"
15
16// SkRecord represents a sequence of SkCanvas calls, saved for future use.
17// These future uses may include: replay, optimization, serialization, or combinations of those.
18//
19// Though an enterprising user may find calling alloc(), append(), visit(), and mutate() enough to
20// work with SkRecord, you probably want to look at SkRecorder which presents an SkCanvas interface
21// for creating an SkRecord, and SkRecordDraw which plays an SkRecord back into another SkCanvas.
22//
23// SkRecord often looks like it's compatible with any type T, but really it's compatible with any
24// type T which has a static const SkRecords::Type kType.  That is to say, SkRecord is compatible
25// only with SkRecords::* structs defined in SkRecords.h.  Your compiler will helpfully yell if you
26// get this wrong.
27
28class SkRecord : public SkNVRefCnt<SkRecord> {
29    enum {
30        // TODO: tune these two constants.
31        kInlineRecords      = 4, // Ideally our lower limit on recorded ops per picture.
32        kInlineAllocLgBytes = 8, // 1<<8 == 256 bytes inline, then SkVarAlloc starting at 512 bytes.
33    };
34public:
35    SkRecord()
36        : fCount(0)
37        , fReserved(kInlineRecords)
38        , fAlloc(kInlineAllocLgBytes+1,  // First malloc'd block is 2x as large as fInlineAlloc.
39                 fInlineAlloc, sizeof(fInlineAlloc)) {}
40    ~SkRecord();
41
42    // Returns the number of canvas commands in this SkRecord.
43    unsigned count() const { return fCount; }
44
45    // Visit the i-th canvas command with a functor matching this interface:
46    //   template <typename T>
47    //   R operator()(const T& record) { ... }
48    // This operator() must be defined for at least all SkRecords::*.
49    template <typename R, typename F>
50    R visit(unsigned i, F& f) const {
51        SkASSERT(i < this->count());
52        return fRecords[i].visit<R>(f);
53    }
54
55    // Mutate the i-th canvas command with a functor matching this interface:
56    //   template <typename T>
57    //   R operator()(T* record) { ... }
58    // This operator() must be defined for at least all SkRecords::*.
59    template <typename R, typename F>
60    R mutate(unsigned i, F& f) {
61        SkASSERT(i < this->count());
62        return fRecords[i].mutate<R>(f);
63    }
64
65    // TODO: It'd be nice to infer R from F for visit and mutate.
66
67    // Allocate contiguous space for count Ts, to be freed when the SkRecord is destroyed.
68    // Here T can be any class, not just those from SkRecords.  Throws on failure.
69    template <typename T>
70    T* alloc(size_t count = 1) {
71        return (T*)fAlloc.alloc(sizeof(T) * count, SK_MALLOC_THROW);
72    }
73
74    // Add a new command of type T to the end of this SkRecord.
75    // You are expected to placement new an object of type T onto this pointer.
76    template <typename T>
77    T* append() {
78        if (fCount == fReserved) {
79            this->grow();
80        }
81        return fRecords[fCount++].set(this->allocCommand<T>());
82    }
83
84    // Replace the i-th command with a new command of type T.
85    // You are expected to placement new an object of type T onto this pointer.
86    // References to the original command are invalidated.
87    template <typename T>
88    T* replace(unsigned i) {
89        SkASSERT(i < this->count());
90
91        Destroyer destroyer;
92        this->mutate<void>(i, destroyer);
93
94        return fRecords[i].set(this->allocCommand<T>());
95    }
96
97    // Replace the i-th command with a new command of type T.
98    // You are expected to placement new an object of type T onto this pointer.
99    // You must show proof that you've already adopted the existing command.
100    template <typename T, typename Existing>
101    T* replace(unsigned i, const SkRecords::Adopted<Existing>& proofOfAdoption) {
102        SkASSERT(i < this->count());
103
104        SkASSERT(Existing::kType == fRecords[i].type());
105        SkASSERT(proofOfAdoption == fRecords[i].ptr());
106
107        return fRecords[i].set(this->allocCommand<T>());
108    }
109
110    // Does not return the bytes in any pointers embedded in the Records; callers
111    // need to iterate with a visitor to measure those they care for.
112    size_t bytesUsed() const;
113
114private:
115    // An SkRecord is structured as an array of pointers into a big chunk of memory where
116    // records representing each canvas draw call are stored:
117    //
118    // fRecords:  [*][*][*]...
119    //             |  |  |
120    //             |  |  |
121    //             |  |  +---------------------------------------+
122    //             |  +-----------------+                        |
123    //             |                    |                        |
124    //             v                    v                        v
125    //   fAlloc:  [SkRecords::DrawRect][SkRecords::DrawPosTextH][SkRecords::DrawRect]...
126    //
127    // We store the types of each of the pointers alongside the pointer.
128    // The cost to append a T to this structure is 8 + sizeof(T) bytes.
129
130    // A mutator that can be used with replace to destroy canvas commands.
131    struct Destroyer {
132        template <typename T>
133        void operator()(T* record) { record->~T(); }
134    };
135
136    template <typename T>
137    SK_WHEN(SkTIsEmpty<T>, T*) allocCommand() {
138        static T singleton = {};
139        return &singleton;
140    }
141
142    template <typename T>
143    SK_WHEN(!SkTIsEmpty<T>, T*) allocCommand() { return this->alloc<T>(); }
144
145    void grow();
146
147    // A typed pointer to some bytes in fAlloc.  visit() and mutate() allow polymorphic dispatch.
148    struct Record {
149        // On 32-bit machines we store type in 4 bytes, followed by a pointer.  Simple.
150        // On 64-bit machines we store a pointer with the type slotted into two top (unused) bytes.
151        // FWIW, SkRecords::Type is tiny.  It can easily fit in one byte.
152        uint64_t fTypeAndPtr;
153        static const int kTypeShift = sizeof(void*) == 4 ? 32 : 48;
154
155        // Point this record to its data in fAlloc.  Returns ptr for convenience.
156        template <typename T>
157        T* set(T* ptr) {
158            fTypeAndPtr = ((uint64_t)T::kType) << kTypeShift | (uintptr_t)ptr;
159            SkASSERT(this->ptr() == ptr && this->type() == T::kType);
160            return ptr;
161        }
162
163        SkRecords::Type type() const { return (SkRecords::Type)(fTypeAndPtr >> kTypeShift); }
164        void* ptr() const { return (void*)(fTypeAndPtr & ((1ull<<kTypeShift)-1)); }
165
166        // Visit this record with functor F (see public API above).
167        template <typename R, typename F>
168        R visit(F& f) const {
169        #define CASE(T) case SkRecords::T##_Type: return f(*(const SkRecords::T*)this->ptr());
170            switch(this->type()) { SK_RECORD_TYPES(CASE) }
171        #undef CASE
172            SkDEBUGFAIL("Unreachable");
173            return R();
174        }
175
176        // Mutate this record with functor F (see public API above).
177        template <typename R, typename F>
178        R mutate(F& f) {
179        #define CASE(T) case SkRecords::T##_Type: return f((SkRecords::T*)this->ptr());
180            switch(this->type()) { SK_RECORD_TYPES(CASE) }
181        #undef CASE
182            SkDEBUGFAIL("Unreachable");
183            return R();
184        }
185    };
186
187    // fRecords needs to be a data structure that can append fixed length data, and need to
188    // support efficient random access and forward iteration.  (It doesn't need to be contiguous.)
189    unsigned fCount, fReserved;
190    SkAutoSTMalloc<kInlineRecords, Record> fRecords;
191
192    // fAlloc needs to be a data structure which can append variable length data in contiguous
193    // chunks, returning a stable handle to that data for later retrieval.
194    SkVarAlloc fAlloc;
195    char fInlineAlloc[1 << kInlineAllocLgBytes];
196};
197
198#endif//SkRecord_DEFINED
199