Repository navigation
Expand file tree
/
Copy pathResourceKey.h
More file actions
357 lines (296 loc) · 13.1 KB
/
Copy pathResourceKey.h
File metadata and controls
357 lines (296 loc) · 13.1 KB
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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
/*
* Copyright 2014 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef skgpu_ResourceKey_DEFINED
#define skgpu_ResourceKey_DEFINED
#include "include/core/SkData.h"
#include "include/core/SkString.h"
#include "include/private/base/SkAlign.h"
#include "include/private/base/SkAlignedStorage.h"
#include "include/private/base/SkOnce.h"
#include "include/private/base/SkTemplates.h"
#include "include/private/base/SkTo.h"
#include <new>
class TestResource;
namespace skgpu {
uint32_t ResourceKeyHash(const uint32_t* data, size_t size);
/**
* Base class for all gpu Resource cache keys. There are two types of cache keys. Refer to the
* comments for each key type below.
*/
class ResourceKey {
public:
uint32_t hash() const {
this->validate();
return fKey[kHash_MetaDataIdx];
}
size_t size() const {
this->validate();
SkASSERT(this->isValid());
return this->internalSize();
}
/** Reset to an invalid key. */
void reset() {
fKey.reset(kMetaDataCnt);
fKey[kHash_MetaDataIdx] = 0;
fKey[kDomainAndSize_MetaDataIdx] = kInvalidDomain;
}
bool isValid() const { return kInvalidDomain != this->domain(); }
/** Used to initialize a key. */
class Builder {
public:
~Builder() { this->finish(); }
void finish() {
if (nullptr == fKey) {
return;
}
uint32_t* hash = &fKey->fKey[kHash_MetaDataIdx];
*hash = ResourceKeyHash(hash + 1, fKey->internalSize() - sizeof(uint32_t));
fKey->validate();
fKey = nullptr;
}
uint32_t& operator[](int dataIdx) {
SkASSERT(fKey);
SkDEBUGCODE(size_t dataCount = fKey->internalSize() / sizeof(uint32_t) - kMetaDataCnt;)
SkASSERT(SkToU32(dataIdx) < dataCount);
return fKey->fKey[(int)kMetaDataCnt + dataIdx];
}
protected:
Builder(ResourceKey* key, uint32_t domain, int data32Count) : fKey(key) {
size_t count = SkToSizeT(data32Count);
SkASSERT(domain != kInvalidDomain);
key->fKey.reset(kMetaDataCnt + count);
size_t size = (count + kMetaDataCnt) * sizeof(uint32_t);
SkASSERT(SkToU16(size) == size);
SkASSERT(SkToU16(domain) == domain);
key->fKey[kDomainAndSize_MetaDataIdx] = SkToU32(domain | (size << 16));
}
private:
ResourceKey* fKey;
};
protected:
static const uint32_t kInvalidDomain = 0;
ResourceKey() { this->reset(); }
bool operator==(const ResourceKey& that) const {
// Both keys should be sized to at least contain the meta data. The metadata contains each
// key's length. So the second memcmp should only run if the keys have the same length.
return 0 == memcmp(fKey.get(), that.fKey.get(), kMetaDataCnt*sizeof(uint32_t)) &&
0 == memcmp(&fKey[kMetaDataCnt], &that.fKey[kMetaDataCnt], this->dataSize());
}
ResourceKey& operator=(const ResourceKey& that) {
if (this != &that) {
if (!that.isValid()) {
this->reset();
} else {
size_t bytes = that.size();
SkASSERT(SkIsAlign4(bytes));
fKey.reset(bytes / sizeof(uint32_t));
memcpy(fKey.get(), that.fKey.get(), bytes);
this->validate();
}
}
return *this;
}
uint32_t domain() const { return fKey[kDomainAndSize_MetaDataIdx] & 0xffff; }
/** size of the key data, excluding meta-data (hash, domain, etc). */
size_t dataSize() const { return this->size() - 4 * kMetaDataCnt; }
/** ptr to the key data, excluding meta-data (hash, domain, etc). */
const uint32_t* data() const {
this->validate();
return &fKey[kMetaDataCnt];
}
#ifdef SK_DEBUG
void dump() const {
if (!this->isValid()) {
SkDebugf("Invalid Key\n");
} else {
SkDebugf("hash: %d ", this->hash());
SkDebugf("domain: %d ", this->domain());
SkDebugf("size: %zuB ", this->internalSize());
size_t dataCount = this->internalSize() / sizeof(uint32_t) - kMetaDataCnt;
for (size_t i = 0; i < dataCount; ++i) {
SkDebugf("%d ", fKey[SkTo<int>(kMetaDataCnt+i)]);
}
SkDebugf("\n");
}
}
#endif
private:
enum MetaDataIdx {
kHash_MetaDataIdx,
// The key domain and size are packed into a single uint32_t.
kDomainAndSize_MetaDataIdx,
kLastMetaDataIdx = kDomainAndSize_MetaDataIdx
};
static const uint32_t kMetaDataCnt = kLastMetaDataIdx + 1;
size_t internalSize() const { return fKey[kDomainAndSize_MetaDataIdx] >> 16; }
void validate() const {
SkASSERT(this->isValid());
SkASSERT(fKey[kHash_MetaDataIdx] ==
ResourceKeyHash(&fKey[kHash_MetaDataIdx] + 1,
this->internalSize() - sizeof(uint32_t)));
SkASSERT(SkIsAlign4(this->internalSize()));
}
friend class ::TestResource; // For unit test to access kMetaDataCnt.
// bmp textures require 5 uint32_t values.
skia_private::AutoSTMalloc<kMetaDataCnt + 5, uint32_t> fKey;
};
/**
* A key used for scratch resources. There are three important rules about scratch keys:
* * Multiple resources can share the same scratch key. Therefore resources assigned the same
* scratch key should be interchangeable with respect to the code that uses them.
* * A resource can have at most one scratch key and it is set at resource creation by the
* resource itself.
* * When a scratch resource is ref'ed it will not be returned from the
* cache for a subsequent cache request until all refs are released. This facilitates using
* a scratch key for multiple render-to-texture scenarios. An example is a separable blur:
*
* GrTexture* texture[2];
* texture[0] = get_scratch_texture(scratchKey);
* texture[1] = get_scratch_texture(scratchKey); // texture[0] is already owned so we will get a
* // different one for texture[1]
* draw_mask(texture[0], path); // draws path mask to texture[0]
* blur_x(texture[0], texture[1]); // blurs texture[0] in y and stores result in texture[1]
* blur_y(texture[1], texture[0]); // blurs texture[1] in y and stores result in texture[0]
* texture[1]->unref(); // texture 1 can now be recycled for the next request with scratchKey
* consume_blur(texture[0]);
* texture[0]->unref(); // texture 0 can now be recycled for the next request with scratchKey
*/
class ScratchKey : public ResourceKey {
public:
/** Uniquely identifies the type of resource that is cached as scratch. */
typedef uint32_t ResourceType;
/** Generate a unique ResourceType. */
static ResourceType GenerateResourceType();
/** Creates an invalid scratch key. It must be initialized using a Builder object before use. */
ScratchKey() {}
ScratchKey(const ScratchKey& that) { *this = that; }
ResourceType resourceType() const { return this->domain(); }
ScratchKey& operator=(const ScratchKey& that) {
this->ResourceKey::operator=(that);
return *this;
}
bool operator==(const ScratchKey& that) const { return this->ResourceKey::operator==(that); }
bool operator!=(const ScratchKey& that) const { return !(*this == that); }
class Builder : public ResourceKey::Builder {
public:
Builder(ScratchKey* key, ResourceType type, int data32Count)
: ResourceKey::Builder(key, type, data32Count) {}
};
};
/**
* A key that allows for exclusive use of a resource for a use case (AKA "domain"). There are three
* rules governing the use of unique keys:
* * Only one resource can have a given unique key at a time. Hence, "unique".
* * A resource can have at most one unique key at a time.
* * Unlike scratch keys, multiple requests for a unique key will return the same
* resource even if the resource already has refs.
* This key type allows a code path to create cached resources for which it is the exclusive user.
* The code path creates a domain which it sets on its keys. This guarantees that there are no
* cross-domain collisions.
*
* Unique keys preempt scratch keys. While a resource has a unique key it is inaccessible via its
* scratch key. It can become scratch again if the unique key is removed.
*/
class UniqueKey : public ResourceKey {
public:
typedef uint32_t Domain;
/** Generate a Domain for unique keys. */
static Domain GenerateDomain();
/** Creates an invalid unique key. It must be initialized using a Builder object before use. */
UniqueKey() : fTag(nullptr) {}
UniqueKey(const UniqueKey& that) { *this = that; }
UniqueKey& operator=(const UniqueKey& that) {
this->ResourceKey::operator=(that);
this->setCustomData(sk_ref_sp(that.getCustomData()));
fTag = that.fTag;
return *this;
}
bool operator==(const UniqueKey& that) const { return this->ResourceKey::operator==(that); }
bool operator!=(const UniqueKey& that) const { return !(*this == that); }
void setCustomData(sk_sp<SkData> data) { fData = std::move(data); }
SkData* getCustomData() const { return fData.get(); }
sk_sp<SkData> refCustomData() const { return fData; }
const char* tag() const { return fTag; }
#ifdef SK_DEBUG
void dump(const char* label) const {
SkDebugf("%s tag: %s\n", label, fTag ? fTag : "None");
this->ResourceKey::dump();
}
#endif
class Builder : public ResourceKey::Builder {
public:
Builder(UniqueKey* key, Domain type, int data32Count, const char* tag = nullptr)
: ResourceKey::Builder(key, type, data32Count) {
key->fTag = tag;
}
/** Used to build a key that wraps another key and adds additional data. */
Builder(UniqueKey* key, const UniqueKey& innerKey, Domain domain, int extraData32Cnt,
const char* tag = nullptr)
: ResourceKey::Builder(key,
domain,
Data32CntForInnerKey(innerKey) + extraData32Cnt) {
SkASSERT(&innerKey != key);
// add the inner key to the end of the key so that op[] can be indexed normally.
uint32_t* innerKeyData = &this->operator[](extraData32Cnt);
const uint32_t* srcData = innerKey.data();
(*innerKeyData++) = innerKey.domain();
memcpy(innerKeyData, srcData, innerKey.dataSize());
key->fTag = tag;
}
private:
static int Data32CntForInnerKey(const UniqueKey& innerKey) {
// key data + domain
return SkToInt((innerKey.dataSize() >> 2) + 1);
}
};
private:
sk_sp<SkData> fData;
const char* fTag;
};
/**
* It is common to need a frequently reused UniqueKey where the only requirement is that the key
* is unique. These macros create such a key in a thread safe manner so the key can be truly global
* and only constructed once.
*/
/** Place outside of function/class definitions. */
#define SKGPU_DECLARE_STATIC_UNIQUE_KEY(name) static SkOnce name##_once
/** Place inside function where the key is used. */
#define SKGPU_DEFINE_STATIC_UNIQUE_KEY(name) \
static SkAlignedSTStorage<1, skgpu::UniqueKey> name##_storage; \
name##_once(skgpu::skgpu_init_static_unique_key_once, &name##_storage); \
static const skgpu::UniqueKey& name = \
*reinterpret_cast<skgpu::UniqueKey*>(name##_storage.get())
static inline void skgpu_init_static_unique_key_once(SkAlignedSTStorage<1, UniqueKey>* keyStorage) {
UniqueKey* key = new (keyStorage->get()) UniqueKey;
UniqueKey::Builder builder(key, UniqueKey::GenerateDomain(), 0);
}
// The cache listens for these messages to purge junk resources proactively.
class UniqueKeyInvalidatedMessage {
public:
UniqueKeyInvalidatedMessage() = default;
UniqueKeyInvalidatedMessage(const UniqueKey& key,
uint32_t contextUniqueID,
bool inThreadSafeCache = false)
: fKey(key), fContextID(contextUniqueID), fInThreadSafeCache(inThreadSafeCache) {
SkASSERT(SK_InvalidUniqueID != contextUniqueID);
}
UniqueKeyInvalidatedMessage(const UniqueKeyInvalidatedMessage&) = default;
UniqueKeyInvalidatedMessage& operator=(const UniqueKeyInvalidatedMessage&) = default;
const UniqueKey& key() const { return fKey; }
uint32_t contextID() const { return fContextID; }
bool inThreadSafeCache() const { return fInThreadSafeCache; }
private:
UniqueKey fKey;
uint32_t fContextID = SK_InvalidUniqueID;
bool fInThreadSafeCache = false;
};
static inline bool SkShouldPostMessageToBus(const UniqueKeyInvalidatedMessage& msg,
uint32_t msgBusUniqueID) {
return msg.contextID() == msgBusUniqueID;
}
} // namespace skgpu
#endif // skgpu_ResourceKey_DEFINED