Home | History | Annotate | Download | only in core
      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 "SkChunkAlloc.h"
     12 #include "SkRecords.h"
     13 #include "SkTLogic.h"
     14 #include "SkTemplates.h"
     15 
     16 // SkRecord (REC-ord) 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 
     28 class SkRecord : SkNoncopyable {
     29 public:
     30     SkRecord(size_t chunkBytes = 4096, unsigned firstReserveCount = 64 / sizeof(void*))
     31         : fAlloc(chunkBytes), fCount(0), fReserved(0), kFirstReserveCount(firstReserveCount) {}
     32 
     33     ~SkRecord() {
     34         Destroyer destroyer;
     35         for (unsigned i = 0; i < this->count(); i++) {
     36             this->mutate<void>(i, destroyer);
     37         }
     38     }
     39 
     40     // Returns the number of canvas commands in this SkRecord.
     41     unsigned count() const { return fCount; }
     42 
     43     // Visit the i-th canvas command with a functor matching this interface:
     44     //   template <typename T>
     45     //   R operator()(const T& record) { ... }
     46     // This operator() must be defined for at least all SkRecords::*.
     47     template <typename R, typename F>
     48     R visit(unsigned i, F& f) const {
     49         SkASSERT(i < this->count());
     50         return fRecords[i].visit<R>(fTypes[i], f);
     51     }
     52 
     53     // Mutate the i-th canvas command with a functor matching this interface:
     54     //   template <typename T>
     55     //   R operator()(T* record) { ... }
     56     // This operator() must be defined for at least all SkRecords::*.
     57     template <typename R, typename F>
     58     R mutate(unsigned i, F& f) {
     59         SkASSERT(i < this->count());
     60         return fRecords[i].mutate<R>(fTypes[i], f);
     61     }
     62     // TODO: It'd be nice to infer R from F for visit and mutate if we ever get std::result_of.
     63 
     64     // Allocate contiguous space for count Ts, to be freed when the SkRecord is destroyed.
     65     // Here T can be any class, not just those from SkRecords.  Throws on failure.
     66     template <typename T>
     67     T* alloc(unsigned count = 1) {
     68         return (T*)fAlloc.allocThrow(sizeof(T) * count);
     69     }
     70 
     71     // Add a new command of type T to the end of this SkRecord.
     72     // You are expected to placement new an object of type T onto this pointer.
     73     template <typename T>
     74     T* append() {
     75         if (fCount == fReserved) {
     76             fReserved = SkTMax(kFirstReserveCount, fReserved*2);
     77             fRecords.realloc(fReserved);
     78             fTypes.realloc(fReserved);
     79         }
     80 
     81         fTypes[fCount] = T::kType;
     82         return fRecords[fCount++].set(this->allocCommand<T>());
     83     }
     84 
     85     // Replace the i-th command with a new command of type T.
     86     // You are expected to placement new an object of type T onto this pointer.
     87     // References to the original command are invalidated.
     88     template <typename T>
     89     T* replace(unsigned i) {
     90         SkASSERT(i < this->count());
     91 
     92         Destroyer destroyer;
     93         this->mutate<void>(i, destroyer);
     94 
     95         fTypes[i] = T::kType;
     96         return fRecords[i].set(this->allocCommand<T>());
     97     }
     98 
     99     // Replace the i-th command with a new command of type T.
    100     // You are expected to placement new an object of type T onto this pointer.
    101     // You must show proof that you've already adopted the existing command.
    102     template <typename T, typename Existing>
    103     T* replace(unsigned i, const SkRecords::Adopted<Existing>& proofOfAdoption) {
    104         SkASSERT(i < this->count());
    105 
    106         SkASSERT(Existing::kType == fTypes[i]);
    107         SkASSERT(proofOfAdoption == fRecords[i].ptr<Existing>());
    108 
    109         fTypes[i] = T::kType;
    110         return fRecords[i].set(this->allocCommand<T>());
    111     }
    112 
    113 private:
    114     // Implementation notes!
    115     //
    116     // Logically an SkRecord is structured as an array of pointers into a big chunk of memory where
    117     // records representing each canvas draw call are stored:
    118     //
    119     // fRecords:  [*][*][*]...
    120     //             |  |  |
    121     //             |  |  |
    122     //             |  |  +---------------------------------------+
    123     //             |  +-----------------+                        |
    124     //             |                    |                        |
    125     //             v                    v                        v
    126     //   fAlloc:  [SkRecords::DrawRect][SkRecords::DrawPosTextH][SkRecords::DrawRect]...
    127     //
    128     // In the scheme above, the pointers in fRecords are void*: they have no type.  The type is not
    129     // stored in fAlloc either; we just write raw data there.  But we need that type information.
    130     // Here are some options:
    131     //   1) use inheritance, virtuals, and vtables to make the fRecords pointers smarter
    132     //   2) store the type data manually in fAlloc at the start of each record
    133     //   3) store the type data manually somewhere with fRecords
    134     //
    135     // This code uses approach 3).  The implementation feels very similar to 1), but it's
    136     // devirtualized instead of using the language's polymorphism mechanisms.  This lets us work
    137     // with the types themselves (as SkRecords::Type), a sort of limited free RTTI; it lets us pay
    138     // only 1 byte to store the type instead of a full pointer (4-8 bytes); and it leads to better
    139     // decoupling between the SkRecords::* record types and the operations performed on them in
    140     // visit() or mutate().  The recorded canvas calls don't have to have any idea about the
    141     // operations performed on them.
    142     //
    143     // We store the types in a parallel fTypes array, mainly so that they can be tightly packed as
    144     // single bytes.  This has the side effect of allowing very fast analysis passes over an
    145     // SkRecord looking for just patterns of draw commands (or using this as a quick reject
    146     // mechanism) though there's admittedly not a very good API exposed publically for this.
    147     //
    148     // The cost to append a T into this structure is 1 + sizeof(void*) + sizeof(T).
    149 
    150     // A mutator that can be used with replace to destroy canvas commands.
    151     struct Destroyer {
    152         template <typename T>
    153         void operator()(T* record) { record->~T(); }
    154     };
    155 
    156     // Logically the same as SkRecords::Type, but packed into 8 bits.
    157     struct Type8 {
    158     public:
    159         // This intentionally converts implicitly back and forth.
    160         Type8(SkRecords::Type type) : fType(type) { SkASSERT(*this == type); }
    161         operator SkRecords::Type () { return (SkRecords::Type)fType; }
    162 
    163     private:
    164         uint8_t fType;
    165     };
    166 
    167     // No point in allocating any more than one of an empty struct.
    168     // We could just return NULL but it's sort of confusing to return NULL on success.
    169     template <typename T>
    170     SK_WHEN(SkTIsEmpty<T>, T*) allocCommand() {
    171         static T singleton = {};
    172         return &singleton;
    173     }
    174 
    175     template <typename T>
    176     SK_WHEN(!SkTIsEmpty<T>, T*) allocCommand() { return this->alloc<T>(); }
    177 
    178     // An untyped pointer to some bytes in fAlloc.  This is the interface for polymorphic dispatch:
    179     // visit() and mutate() work with the parallel fTypes array to do the work of a vtable.
    180     struct Record {
    181     public:
    182         // Point this record to its data in fAlloc.  Returns ptr for convenience.
    183         template <typename T>
    184         T* set(T* ptr) {
    185             fPtr = ptr;
    186             return ptr;
    187         }
    188 
    189         // Get the data in fAlloc, assuming it's of type T.
    190         template <typename T>
    191         T* ptr() const { return (T*)fPtr; }
    192 
    193         // Visit this record with functor F (see public API above) assuming the record we're
    194         // pointing to has this type.
    195         template <typename R, typename F>
    196         R visit(Type8 type, F& f) const {
    197         #define CASE(T) case SkRecords::T##_Type: return f(*this->ptr<SkRecords::T>());
    198             switch(type) { SK_RECORD_TYPES(CASE) }
    199         #undef CASE
    200             SkDEBUGFAIL("Unreachable");
    201             return R();
    202         }
    203 
    204         // Mutate this record with functor F (see public API above) assuming the record we're
    205         // pointing to has this type.
    206         template <typename R, typename F>
    207         R mutate(Type8 type, F& f) {
    208         #define CASE(T) case SkRecords::T##_Type: return f(this->ptr<SkRecords::T>());
    209             switch(type) { SK_RECORD_TYPES(CASE) }
    210         #undef CASE
    211             SkDEBUGFAIL("Unreachable");
    212             return R();
    213         }
    214 
    215     private:
    216         void* fPtr;
    217     };
    218 
    219     // fAlloc needs to be a data structure which can append variable length data in contiguous
    220     // chunks, returning a stable handle to that data for later retrieval.
    221     //
    222     // fRecords and fTypes need to be data structures that can append fixed length data, and need to
    223     // support efficient forward iteration.  (They don't need to be contiguous or indexable.)
    224 
    225     SkChunkAlloc fAlloc;
    226     SkAutoTMalloc<Record> fRecords;
    227     SkAutoTMalloc<Type8> fTypes;
    228     // fCount and fReserved measure both fRecords and fTypes, which always grow in lock step.
    229     unsigned fCount;
    230     unsigned fReserved;
    231     const unsigned kFirstReserveCount;
    232 };
    233 
    234 #endif//SkRecord_DEFINED
    235