Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SkRecord.h
Go to the documentation of this file.
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
15#include "src/core/SkRecords.h"
16
17#include <cstddef>
18#include <type_traits>
19
20// SkRecord represents a sequence of SkCanvas calls, saved for future use.
21// These future uses may include: replay, optimization, serialization, or combinations of those.
22//
23// Though an enterprising user may find calling alloc(), append(), visit(), and mutate() enough to
24// work with SkRecord, you probably want to look at SkRecorder which presents an SkCanvas interface
25// for creating an SkRecord, and SkRecordDraw which plays an SkRecord back into another SkCanvas.
26//
27// SkRecord often looks like it's compatible with any type T, but really it's compatible with any
28// type T which has a static const SkRecords::Type kType. That is to say, SkRecord is compatible
29// only with SkRecords::* structs defined in SkRecords.h. Your compiler will helpfully yell if you
30// get this wrong.
31
32class SkRecord : public SkRefCnt {
33public:
34 SkRecord() = default;
35 ~SkRecord() override;
36
37 // Returns the number of canvas commands in this SkRecord.
38 int count() const { return fCount; }
39
40 // Visit the i-th canvas command with a functor matching this interface:
41 // template <typename T>
42 // R operator()(const T& record) { ... }
43 // This operator() must be defined for at least all SkRecords::*.
44 template <typename F>
45 auto visit(int i, F&& f) const -> decltype(f(SkRecords::NoOp())) {
46 return fRecords[i].visit(f);
47 }
48
49 // Mutate the i-th canvas command with a functor matching this interface:
50 // template <typename T>
51 // R operator()(T* record) { ... }
52 // This operator() must be defined for at least all SkRecords::*.
53 template <typename F>
54 auto mutate(int i, F&& f) -> decltype(f((SkRecords::NoOp*)nullptr)) {
55 return fRecords[i].mutate(f);
56 }
57
58 // Allocate contiguous space for count Ts, to be freed when the SkRecord is destroyed.
59 // Here T can be any class, not just those from SkRecords. Throws on failure.
60 template <typename T>
61 T* alloc(size_t count = 1) {
62 struct RawBytes {
63 alignas(T) char data[sizeof(T)];
64 };
65 fApproxBytesAllocated += count * sizeof(T) + alignof(T);
66 return (T*)fAlloc.makeArrayDefault<RawBytes>(count);
67 }
68
69 // Add a new command of type T to the end of this SkRecord.
70 // You are expected to placement new an object of type T onto this pointer.
71 template <typename T>
72 T* append() {
73 if (fCount == fReserved) {
74 this->grow();
75 }
76 return fRecords[fCount++].set(this->allocCommand<T>());
77 }
78
79 // Replace the i-th command with a new command of type T.
80 // You are expected to placement new an object of type T onto this pointer.
81 // References to the original command are invalidated.
82 template <typename T>
83 T* replace(int i) {
84 SkASSERT(i < this->count());
85
86 Destroyer destroyer;
87 this->mutate(i, destroyer);
88
89 return fRecords[i].set(this->allocCommand<T>());
90 }
91
92 // Does not return the bytes in any pointers embedded in the Records; callers
93 // need to iterate with a visitor to measure those they care for.
94 size_t bytesUsed() const;
95
96 // Rearrange and resize this record to eliminate any NoOps.
97 // May change count() and the indices of ops, but preserves their order.
98 void defrag();
99
100private:
101 // An SkRecord is structured as an array of pointers into a big chunk of memory where
102 // records representing each canvas draw call are stored:
103 //
104 // fRecords: [*][*][*]...
105 // | | |
106 // | | |
107 // | | +---------------------------------------+
108 // | +-----------------+ |
109 // | | |
110 // v v v
111 // fAlloc: [SkRecords::DrawRect][SkRecords::DrawPosTextH][SkRecords::DrawRect]...
112 //
113 // We store the types of each of the pointers alongside the pointer.
114 // The cost to append a T to this structure is 8 + sizeof(T) bytes.
115
116 // A mutator that can be used with replace to destroy canvas commands.
117 struct Destroyer {
118 template <typename T>
119 void operator()(T* record) { record->~T(); }
120 };
121
122 template <typename T>
123 std::enable_if_t<std::is_empty<T>::value, T*> allocCommand() {
124 static T singleton = {};
125 return &singleton;
126 }
127
128 template <typename T>
129 std::enable_if_t<!std::is_empty<T>::value, T*> allocCommand() { return this->alloc<T>(); }
130
131 void grow();
132
133 // A typed pointer to some bytes in fAlloc. visit() and mutate() allow polymorphic dispatch.
134 struct Record {
135 SkRecords::Type fType;
136 void* fPtr;
137
138 // Point this record to its data in fAlloc. Returns ptr for convenience.
139 template <typename T>
140 T* set(T* ptr) {
141 fType = T::kType;
142 fPtr = ptr;
143 SkASSERT(this->ptr() == ptr && this->type() == T::kType);
144 return ptr;
145 }
146
147 SkRecords::Type type() const { return fType; }
148 void* ptr() const { return fPtr; }
149
150 // Visit this record with functor F (see public API above).
151 template <typename F>
152 auto visit(F&& f) const -> decltype(f(SkRecords::NoOp())) {
153 #define CASE(T) case SkRecords::T##_Type: return f(*(const SkRecords::T*)this->ptr());
154 switch(this->type()) { SK_RECORD_TYPES(CASE) }
155 #undef CASE
156 SkDEBUGFAIL("Unreachable");
157 static const SkRecords::NoOp noop{};
158 return f(noop);
159 }
160
161 // Mutate this record with functor F (see public API above).
162 template <typename F>
163 auto mutate(F&& f) -> decltype(f((SkRecords::NoOp*)nullptr)) {
164 #define CASE(T) case SkRecords::T##_Type: return f((SkRecords::T*)this->ptr());
165 switch(this->type()) { SK_RECORD_TYPES(CASE) }
166 #undef CASE
167 SkDEBUGFAIL("Unreachable");
168 static const SkRecords::NoOp noop{};
169 return f(const_cast<SkRecords::NoOp*>(&noop));
170 }
171 };
172
173 // fRecords needs to be a data structure that can append fixed length data, and need to
174 // support efficient random access and forward iteration. (It doesn't need to be contiguous.)
175 int fCount{0},
176 fReserved{0};
178
179 // fAlloc needs to be a data structure which can append variable length data in contiguous
180 // chunks, returning a stable handle to that data for later retrieval.
181 SkArenaAlloc fAlloc{256};
182 size_t fApproxBytesAllocated{0};
183};
184
185#endif//SkRecord_DEFINED
#define SkDEBUGFAIL(message)
Definition SkAssert.h:118
#define SkASSERT(cond)
Definition SkAssert.h:116
#define CASE(T)
#define SK_RECORD_TYPES(M)
Definition SkRecords.h:56
T * makeArrayDefault(size_t count)
T * replace(int i)
Definition SkRecord.h:83
auto visit(int i, F &&f) const -> decltype(f(SkRecords::NoOp()))
Definition SkRecord.h:45
size_t bytesUsed() const
Definition SkRecord.cpp:25
T * alloc(size_t count=1)
Definition SkRecord.h:61
auto mutate(int i, F &&f) -> decltype(f((SkRecords::NoOp *) nullptr))
Definition SkRecord.h:54
int count() const
Definition SkRecord.h:38
void defrag()
Definition SkRecord.cpp:30
T * append()
Definition SkRecord.h:72
SkRecord()=default
~SkRecord() override
Definition SkRecord.cpp:12
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not set
Definition switches.h:76
noop(*args, **kwargs)
#define T
Definition SkMD5.cpp:120