Skip to content

Commit a9b4f1f

Browse files
committed
Persistent run-time-populated MaterialX codegen cache
1 parent b9282cb commit a9b4f1f

15 files changed

+1181
-273
lines changed

pxr/imaging/hd/instanceRegistry.h

Lines changed: 156 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -20,32 +20,37 @@
2020

2121
PXR_NAMESPACE_OPEN_SCOPE
2222

23+
// Forward declarations
24+
template <typename VALUE>
25+
class HdInstanceRegistry;
26+
27+
using HdInstanceKey = uint64_t;
28+
using HdInstanceRegistryMutex = std::mutex;
29+
using HdInstanceRegistryLock = std::unique_lock<HdInstanceRegistryMutex>;
2330

2431
/// \class HdInstance
2532
///
26-
/// This class is used as an interface to a shared instance in
27-
/// HdInstanceRegistry.
33+
/// This class is used as an interface to a shared instance stored in
34+
/// a REGISTRY object.
2835
///
29-
/// KeyType is a hashable index type and VALUE is shared_ptr. In most use
30-
/// cases, the client computes a hash key which represents large bulky data
31-
/// (like topology, primvars) and registers it into HdInstanceRegistry. If the
32-
/// key has already been registered, the registry returns HdInstance and the
36+
/// HdInstanceKeyType is a hashable index type and VALUE is shared_ptr. In most
37+
/// use cases, the client computes a hash key which represents large bulky data
38+
/// (like topology, primvars) and registers it into REGISTRY. If the
39+
/// key has already been registered, the registry returns an HdInstance and the
3340
/// client can use GetValue() without setting/computing actual bulky data. If
3441
/// it doesn't exist, IsFirstInstance() returns true for the first instance
3542
/// and the client needs to populate an appropriate data VALUE into the
3643
/// instance by SetValue().
3744
///
38-
/// In order to support concurrent access to HdInstanceRegistry, this
39-
/// class holds a lock to a mutex in HdInstanceRegistry. This lock will
45+
/// In order to support concurrent access to REGISTRY, this
46+
/// class holds a lock to a mutex in REGISTRY. This lock will
4047
/// be held until the instance of this interface class is destroyed.
4148
///
42-
template <typename VALUE>
49+
template <typename VALUE, class REGISTRY = HdInstanceRegistry<VALUE>>
4350
class HdInstance {
4451
public:
45-
typedef uint64_t KeyType;
4652
typedef VALUE ValueType;
47-
48-
typedef KeyType ID;
53+
typedef HdInstanceKey ID;
4954

5055
struct ValueHolder {
5156
ValueHolder(ValueType const & value = ValueType())
@@ -59,46 +64,44 @@ class HdInstance {
5964
ValueType value;
6065
int recycleCounter;
6166
};
62-
typedef tbb::concurrent_unordered_map<KeyType, ValueHolder> Dictionary;
63-
64-
typedef std::mutex RegistryMutex;
65-
typedef std::unique_lock<RegistryMutex> RegistryLock;
6667

6768
HdInstance() = delete;
6869

6970
/// Construct an instance holding a registry lock, representing a value
7071
/// held in a registry container.
71-
explicit HdInstance(KeyType const &key,
72-
ValueType const &value,
73-
RegistryLock &&registryLock,
74-
Dictionary *container)
72+
explicit HdInstance(HdInstanceKey key,
73+
ValueType const &value,
74+
HdInstanceRegistryLock &&registryLock,
75+
REGISTRY *registry)
7576
: _key(key)
7677
, _value(value)
7778
, _registryLock(std::move(registryLock))
78-
, _container(container)
79+
, _registry(registry)
7980
, _isFirstInstance(!bool(_value))
8081
{ }
8182

8283
/// Construct an instance with no lock or registry container. This
8384
/// is used to present a consistent interface to clients in cases
8485
/// where shared resource registration is disabled.
85-
explicit HdInstance(KeyType const &key)
86+
explicit HdInstance(HdInstanceKey key)
8687
: _key(key)
8788
, _value(ValueType())
8889
, _registryLock()
89-
, _container(nullptr)
90+
, _registry(nullptr)
9091
, _isFirstInstance(!bool(_value))
9192
{ }
9293

9394
/// Returns the key
94-
KeyType const &GetKey() const { return _key; }
95+
HdInstanceKey GetKey() const { return _key; }
9596

9697
/// Returns the value
9798
ValueType const &GetValue() const { return _value; }
9899

99-
/// Update the value in dictionary indexed by the key.
100+
/// Update the value in the registry
100101
void SetValue(ValueType const &value) {
101-
if (_container) (*_container)[_key] = ValueHolder(value);
102+
if (_registry) {
103+
_registry->_SetValue(_key, value);
104+
}
102105
_value = value;
103106
}
104107

@@ -108,45 +111,55 @@ class HdInstance {
108111
}
109112

110113
private:
111-
KeyType _key;
112-
ValueType _value;
113-
RegistryLock _registryLock;
114-
Dictionary *_container;
115-
bool _isFirstInstance;
114+
HdInstanceKey _key;
115+
ValueType _value;
116+
HdInstanceRegistryLock _registryLock;
117+
REGISTRY *_registry = nullptr;
118+
bool _isFirstInstance;
116119
};
117120

118-
/// \class HdInstanceRegistry
121+
/// \class HdInstanceRegistryBase
119122
///
120-
/// HdInstanceRegistry is a dictionary container of HdInstance.
121-
/// This class is almost just a dictionary from key to value.
122-
/// For cleaning unused entries, it provides GarbageCollect() API.
123-
/// It sweeps all entries in the dictionary and erase unreferenced entries.
123+
/// HdInstanceRegistryBase is a dictionary container of HdInstance.
124+
/// This class is mostly just a dictionary from key to value, ensuring
125+
/// thread-safe access to the values.
126+
/// The DERIVED template parameter can be used in a Curiously Recurring
127+
/// Template Pattern to extend this class with a persistent cache backing the
128+
/// in-memory dictionary.
129+
/// For cleaning up unused entries, the GarbageCollect() API is provided.
130+
/// It sweeps all entries in the dictionary and erases unreferenced entries.
124131
/// When HdInstance::ValueType is shared_ptr, it is regarded as unreferenced
125132
/// if the shared_ptr is unique (use_count==1). Note that Key is not
126133
/// involved to determine the lifetime of entries.
127134
///
128-
template <typename VALUE>
129-
class HdInstanceRegistry {
135+
template <typename VALUE, class DERIVED>
136+
class HdInstanceRegistryBase {
130137
public:
131-
typedef HdInstance<VALUE> InstanceType;
132-
133-
HdInstanceRegistry() = default;
138+
friend class HdInstance<VALUE, DERIVED>;
139+
using InstanceType = HdInstance<VALUE, DERIVED>;
134140

135-
/// Copy constructor. Need as HdInstanceRegistry is placed in a map
136-
/// and mutex is not copy constructable, so can't use default
137-
HdInstanceRegistry(const HdInstanceRegistry &other)
141+
using Dictionary = tbb::concurrent_unordered_map<
142+
HdInstanceKey,
143+
typename InstanceType::ValueHolder
144+
>;
145+
146+
HdInstanceRegistryBase() = default;
147+
148+
/// Copy constructor. Need as HdInstanceRegistryBase is placed in a map
149+
/// and mutex is not copy-constructible, so can't use default
150+
HdInstanceRegistryBase(const HdInstanceRegistryBase &other)
138151
: _dictionary(other._dictionary)
139-
, _registryMutex() // mutex is not copied
152+
, _mutex() // mutex is not copied
140153
{ }
141154

142-
/// Returns a shared instance for given key.
155+
/// Returns a shared instance for a given key.
143156
InstanceType GetInstance(
144-
typename InstanceType::KeyType const &key);
157+
HdInstanceKey key);
145158

146159
/// Returns a shared instance for a given key
147160
/// only if the key exists in the dictionary.
148161
InstanceType FindInstance(
149-
typename InstanceType::KeyType const &key, bool *found);
162+
HdInstanceKey key, bool *found);
150163

151164
/// Removes unreferenced entries and returns the count
152165
/// of remaining entries. When recycleCount is greater than zero,
@@ -166,7 +179,7 @@ class HdInstanceRegistry {
166179

167180
/// Returns a const iterator being/end of dictionary. Mainly used for
168181
/// resource auditing.
169-
typedef typename InstanceType::Dictionary::const_iterator const_iterator;
182+
typedef typename Dictionary::const_iterator const_iterator;
170183
const_iterator begin() const { return _dictionary.begin(); }
171184
const_iterator end() const { return _dictionary.end(); }
172185

@@ -180,73 +193,142 @@ class HdInstanceRegistry {
180193
return value.use_count() == 1;
181194
}
182195

183-
typename InstanceType::Dictionary _dictionary;
184-
typename InstanceType::RegistryMutex _registryMutex;
196+
/// Store the value in the in-memory dictionary and write it to the
197+
/// persistent cache if implemented by the derived class.
198+
void _SetValue(
199+
HdInstanceKey key,
200+
VALUE const& value);
185201

186-
HdInstanceRegistry &operator =(HdInstanceRegistry &) = delete;
202+
/// Look a value up by the key in the in-memory dictionary. If the key is
203+
/// not found there and the derived class implements a persistent cache,
204+
/// try to load the value from there.
205+
typename Dictionary::iterator _LookUp(
206+
HdInstanceKey key);
207+
208+
Dictionary _dictionary;
209+
HdInstanceRegistryMutex _mutex;
210+
211+
HdInstanceRegistryBase &operator =(HdInstanceRegistryBase &) = delete;
212+
};
213+
214+
/// \class HdInstanceRegistry
215+
///
216+
/// The default implementation of HdInstanceRegistryBase without persistent
217+
/// cache support.
218+
///
219+
template <typename VALUE>
220+
class HdInstanceRegistry
221+
: public HdInstanceRegistryBase<VALUE, HdInstanceRegistry<VALUE>>
222+
{
223+
public:
224+
friend class HdInstanceRegistryBase<VALUE, HdInstanceRegistry<VALUE>>;
225+
226+
void SaveToDisk(
227+
HdInstanceKey key,
228+
VALUE const& value)
229+
{
230+
}
231+
232+
VALUE LoadFromDisk(HdInstanceKey key)
233+
{
234+
return nullptr;
235+
}
187236
};
188237

189238
// ---------------------------------------------------------------------------
190-
// instance registry impl
239+
// Implementations
191240

192-
template <typename VALUE>
193-
HdInstance<VALUE>
194-
HdInstanceRegistry<VALUE>::GetInstance(
195-
typename HdInstance<VALUE>::KeyType const &key)
241+
template <typename VALUE, class DERIVED>
242+
typename HdInstanceRegistryBase<VALUE, DERIVED>::Dictionary::iterator
243+
HdInstanceRegistryBase<VALUE, DERIVED>::_LookUp(
244+
HdInstanceKey key)
245+
{
246+
typename Dictionary::iterator it = _dictionary.find(key);
247+
if (it != _dictionary.end()) {
248+
return it;
249+
}
250+
251+
VALUE value = static_cast<DERIVED*>(this)->LoadFromDisk(key);
252+
if (value) {
253+
it = _dictionary.insert(
254+
std::make_pair(key,
255+
typename InstanceType::ValueHolder(value))).first;
256+
}
257+
return it;
258+
}
259+
260+
template <typename VALUE, class DERIVED>
261+
void
262+
HdInstanceRegistryBase<VALUE, DERIVED>::_SetValue(
263+
HdInstanceKey key,
264+
VALUE const& value)
265+
{
266+
_dictionary[key] = typename InstanceType::ValueHolder(value);
267+
static_cast<DERIVED*>(this)->SaveToDisk(key, value);
268+
}
269+
270+
template <typename VALUE, class DERIVED>
271+
HdInstance<VALUE, DERIVED>
272+
HdInstanceRegistryBase<VALUE, DERIVED>::GetInstance(
273+
HdInstanceKey key)
196274
{
197275
HD_TRACE_FUNCTION();
198276
HF_MALLOC_TAG_FUNCTION();
199277

200278
// Grab Registry lock
201279
// (and don't release it in this function, return it instead)
202-
typename InstanceType::RegistryLock lock(_registryMutex);
280+
HdInstanceRegistryLock lock(_mutex);
203281

204-
typename InstanceType::Dictionary::iterator it = _dictionary.find(key);
282+
typename Dictionary::iterator it = _LookUp(key);
205283
if (it == _dictionary.end()) {
206284
// not found. create new one
207285
it = _dictionary.insert(
208286
std::make_pair(key, typename InstanceType::ValueHolder())).first;
209287
}
210288

211289
it->second.ResetRecycleCounter();
212-
return InstanceType(key, it->second.value, std::move(lock), &_dictionary);
290+
return InstanceType(
291+
key, it->second.value, std::move(lock), static_cast<DERIVED*>(this));
213292
}
214293

215-
template <typename VALUE>
216-
HdInstance<VALUE>
217-
HdInstanceRegistry<VALUE>::FindInstance(
218-
typename HdInstance<VALUE>::KeyType const &key, bool *found)
294+
template <typename VALUE, class DERIVED>
295+
HdInstance<VALUE, DERIVED>
296+
HdInstanceRegistryBase<VALUE, DERIVED>::FindInstance(
297+
HdInstanceKey key, bool *found)
219298
{
220299
HD_TRACE_FUNCTION();
221300
HF_MALLOC_TAG_FUNCTION();
222301

223302
// Grab Registry lock
224303
// (and don't release it in this function, return it instead)
225-
typename InstanceType::RegistryLock lock(_registryMutex);
304+
HdInstanceRegistryLock lock(_mutex);
226305

227-
typename InstanceType::Dictionary::iterator it = _dictionary.find(key);
306+
typename Dictionary::iterator it = _LookUp(key);
228307
if (it == _dictionary.end()) {
229308
*found = false;
230309
return InstanceType(key, VALUE(), std::move(lock), nullptr);
231310
} else {
232311
*found = true;
233312
it->second.ResetRecycleCounter();
234-
return InstanceType(key, it->second.value,std::move(lock),&_dictionary);
313+
return InstanceType(
314+
key, it->second.value, std::move(lock),
315+
static_cast<DERIVED*>(this));
235316
}
236317
}
237318

238-
template <typename VALUE>
319+
template <typename VALUE, class DERIVED>
239320
size_t
240-
HdInstanceRegistry<VALUE>::GarbageCollect(int recycleCount)
321+
HdInstanceRegistryBase<VALUE, DERIVED>::GarbageCollect(int recycleCount)
241322
{
242323
// Call GarbageCollect with empty callback function
243324
return GarbageCollect([](void*){}, recycleCount);
244325
}
245326

246-
template <typename VALUE>
327+
template <typename VALUE, class DERIVED>
247328
template <typename Callback>
248329
size_t
249-
HdInstanceRegistry<VALUE>::GarbageCollect(Callback &&callback, int recycleCount)
330+
HdInstanceRegistryBase<VALUE, DERIVED>::GarbageCollect(
331+
Callback &&callback, int recycleCount)
250332
{
251333
HD_TRACE_FUNCTION();
252334
HF_MALLOC_TAG_FUNCTION();
@@ -257,7 +339,7 @@ HdInstanceRegistry<VALUE>::GarbageCollect(Callback &&callback, int recycleCount)
257339
}
258340

259341
size_t inUseCount = 0;
260-
for (typename InstanceType::Dictionary::iterator it = _dictionary.begin();
342+
for (typename Dictionary::iterator it = _dictionary.begin();
261343
it != _dictionary.end();) {
262344

263345
// erase instance which isn't referred from anyone
@@ -273,9 +355,9 @@ HdInstanceRegistry<VALUE>::GarbageCollect(Callback &&callback, int recycleCount)
273355
return inUseCount;
274356
}
275357

276-
template <typename VALUE>
358+
template <typename VALUE, class DERIVED>
277359
void
278-
HdInstanceRegistry<VALUE>::Invalidate()
360+
HdInstanceRegistryBase<VALUE, DERIVED>::Invalidate()
279361
{
280362
HD_TRACE_FUNCTION();
281363
HF_MALLOC_TAG_FUNCTION();

0 commit comments

Comments
 (0)