1// Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#ifndef QT3DCORE_QABSTRACTRESOURCESMANAGER_H
5#define QT3DCORE_QABSTRACTRESOURCESMANAGER_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists for the convenience
12// of other Qt classes. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <QtCore/QHash>
19#include <QtCore/QMutex>
20#include <QtCore/QReadLocker>
21#include <QtCore/QReadWriteLock>
22#include <QtCore/QtGlobal>
23#include <limits>
24
25#include <Qt3DCore/private/qhandle_p.h>
26#include <Qt3DCore/private/qt3dcore_global_p.h>
27
28// Silence complaints about unreferenced local variables in
29// ArrayAllocatingPolicy::deallocateBuckets() when the compiler
30// inlines the call to the dtor and it is empty. Default warning
31// setting re-enabled at bottom of this file
32#if defined(Q_CC_MSVC)
33#pragma warning(disable : 4189)
34#endif
35
36QT_BEGIN_NAMESPACE
37
38namespace Qt3DCore {
39
40template <class Host>
41struct NonLockingPolicy
42{
43 struct ReadLocker
44 {
45 ReadLocker(const NonLockingPolicy*) {}
46 void unlock() {}
47 void relock() {}
48 };
49
50 struct WriteLocker
51 {
52 WriteLocker(const NonLockingPolicy*) {}
53 void unlock() {}
54 void relock() {}
55 };
56
57 struct Locker
58 {
59 Locker(const NonLockingPolicy*) {}
60 void unlock() {}
61 void relock() {}
62 };
63};
64
65template <class Host>
66class ObjectLevelLockingPolicy
67{
68public :
69 ObjectLevelLockingPolicy()
70 {}
71
72 class ReadLocker
73 {
74 public:
75 ReadLocker(const ObjectLevelLockingPolicy *host)
76 : m_locker(&host->m_readWritelock)
77 { }
78
79 void unlock()
80 {
81 m_locker.unlock();
82 }
83
84 void relock()
85 {
86 m_locker.relock();
87 }
88
89 private:
90 QReadLocker m_locker;
91 };
92
93 class WriteLocker
94 {
95 public:
96 WriteLocker(const ObjectLevelLockingPolicy *host)
97 : m_locker(&host->m_readWritelock)
98 { }
99
100 void unlock()
101 {
102 m_locker.unlock();
103 }
104
105 void relock()
106 {
107 m_locker.relock();
108 }
109
110 private:
111 QWriteLocker m_locker;
112 };
113
114 class Locker
115 {
116 public:
117 Locker(const ObjectLevelLockingPolicy *host)
118 : m_locker(&host->m_lock)
119 { }
120
121 void unlock()
122 {
123 m_locker.unlock();
124 }
125
126 void relock()
127 {
128 m_locker.relock();
129 }
130
131 private:
132 QMutexLocker<QMutex> m_locker;
133 };
134
135private:
136 friend class Locker;
137 friend class ReadLocker;
138 friend class WriteLocker;
139 mutable QReadWriteLock m_readWritelock;
140 mutable QMutex m_lock;
141};
142
143template <typename T>
144struct QResourceInfo
145{
146 static const bool needsCleanup = false;
147};
148
149enum
150{
151 Q_REQUIRES_CLEANUP = 0
152};
153
154#define Q_DECLARE_RESOURCE_INFO(TYPE, FLAGS) \
155 namespace Qt3DCore { \
156 template<> \
157 struct QResourceInfo<TYPE > \
158{ \
159 static const bool needsCleanup = (FLAGS & Q_REQUIRES_CLEANUP) == 0;\
160}; \
161} // namespace Qt3DCore
162
163template<typename T>
164class QHandleData : public QHandle<T>::Data
165{
166public:
167 T data;
168};
169
170template<typename T>
171inline T *QHandle<T>::operator->() const { return (d && counter == d->counter) ? &static_cast<QHandleData<T> *>(d)->data : nullptr; }
172template<typename T>
173inline T *QHandle<T>::data() const { return (d && counter == d->counter) ? &static_cast<QHandleData<T> *>(d)->data : nullptr; }
174
175
176class Q_3DCORE_PRIVATE_EXPORT AlignedAllocator
177{
178public:
179 static void *allocate(uint size);
180 static void release(void *p);
181};
182
183template <typename T>
184class ArrayAllocatingPolicy
185{
186public:
187 typedef QHandleData<T> HandleData;
188 typedef QHandle<T> Handle;
189 ArrayAllocatingPolicy()
190 {
191 }
192
193 ~ArrayAllocatingPolicy()
194 {
195 m_activeHandles.clear();
196 deallocateBuckets();
197 }
198
199 Handle allocateResource()
200 {
201 if (!freeList)
202 allocateBucket();
203 typename Handle::Data *d = freeList;
204 freeList = freeList->nextFree;
205 d->counter = allocCounter;
206 allocCounter += 2; // ensure this will never clash with a pointer in nextFree by keeping the lowest bit set
207 Handle handle(d);
208 m_activeHandles.push_back(handle);
209 return handle;
210 }
211
212 void releaseResource(const Handle &handle)
213 {
214 m_activeHandles.erase(std::remove(m_activeHandles.begin(), m_activeHandles.end(), handle), m_activeHandles.end());
215 typename Handle::Data *d = handle.data_ptr();
216 d->nextFree = freeList;
217 freeList = d;
218 performCleanup(&static_cast<QHandleData<T> *>(d)->data, std::integral_constant<bool, QResourceInfo<T>::needsCleanup>{});
219 }
220
221 T *data(Handle h)
222 {
223 return h.operator->();
224 }
225
226 void for_each(std::function<void(T*)> f)
227 {
228 Bucket *b = firstBucket;
229 while (b) {
230 for (int idx = 0; idx < Bucket::NumEntries; ++idx) {
231 T *t = &b->data[idx].data;
232 f(t);
233 }
234 b = b->header.next;
235 }
236 }
237
238 int count() const { return int(m_activeHandles.size()); }
239 const std::vector<Handle> &activeHandles() const { return m_activeHandles; }
240
241private:
242 Q_DISABLE_COPY(ArrayAllocatingPolicy)
243 struct Bucket
244 {
245 struct Header
246 {
247 Bucket *next;
248 } header;
249 enum {
250 Size = 4096,
251 NumEntries = (Size - sizeof(Header)) / sizeof(HandleData)
252 };
253 HandleData data[NumEntries];
254 };
255
256 Bucket *firstBucket = nullptr;
257 std::vector<Handle> m_activeHandles;
258 typename Handle::Data *freeList = nullptr;
259 int allocCounter = 1;
260
261 void allocateBucket()
262 {
263 // no free handle, allocate a new
264 // allocate aligned memory
265 Bucket *b = static_cast<Bucket *>(AlignedAllocator::allocate(size: sizeof(Bucket)));
266
267 // placement new
268 new (b) Bucket;
269
270 b->header.next = firstBucket;
271 firstBucket = b;
272 for (int i = 0; i < Bucket::NumEntries - 1; ++i) {
273 b->data[i].nextFree = &b->data[i + 1];
274 }
275 b->data[Bucket::NumEntries - 1].nextFree = nullptr;
276 freeList = &b->data[0];
277 }
278
279 void deallocateBuckets()
280 {
281 Bucket *b = firstBucket;
282 while (b) {
283 Bucket *n = b->header.next;
284 // Call dtor explicitly
285 b->~Bucket();
286 // Release aligned memory
287 AlignedAllocator::release(p: b);
288 b = n;
289 }
290 }
291
292 template<typename Q = T>
293 void performCleanup(Q *r, std::integral_constant<bool, true>)
294 {
295 r->cleanup();
296 }
297
298 template<typename Q = T>
299 void performCleanup(Q *, std::integral_constant<bool, false>)
300 {
301 }
302
303};
304
305#ifndef QT_NO_DEBUG_STREAM
306template <typename ValueType, typename KeyType,
307 template <class> class LockingPolicy
308 >
309class QResourceManager;
310
311template <typename ValueType, typename KeyType,
312 template <class> class LockingPolicy = NonLockingPolicy
313 >
314QDebug operator<<(QDebug dbg, const QResourceManager<ValueType, KeyType, LockingPolicy> &manager);
315#endif
316
317template <typename ValueType, typename KeyType,
318 template <class> class LockingPolicy = NonLockingPolicy
319 >
320class QResourceManager
321 : public ArrayAllocatingPolicy<ValueType>
322 , public LockingPolicy< QResourceManager<ValueType, KeyType, LockingPolicy> >
323{
324public:
325 typedef ArrayAllocatingPolicy<ValueType> Allocator;
326 typedef QHandle<ValueType> Handle;
327
328 QResourceManager() :
329 Allocator()
330 {
331 }
332
333 ~QResourceManager()
334 {}
335
336 Handle acquire()
337 {
338 typename LockingPolicy<QResourceManager>::WriteLocker lock(this);
339 return Allocator::allocateResource();
340 }
341
342 ValueType* data(const Handle &handle)
343 {
344 return handle.operator->();
345 }
346
347 void release(const Handle &handle)
348 {
349 typename LockingPolicy<QResourceManager>::WriteLocker lock(this);
350 Allocator::releaseResource(handle);
351 }
352
353 bool contains(const KeyType &id) const
354 {
355 typename LockingPolicy<QResourceManager>::ReadLocker lock(this);
356 return m_keyToHandleMap.contains(id);
357 }
358
359 Handle getOrAcquireHandle(const KeyType &id)
360 {
361 typename LockingPolicy<QResourceManager>::ReadLocker lock(this);
362 Handle handle = m_keyToHandleMap.value(id);
363 if (handle.isNull()) {
364 lock.unlock();
365 typename LockingPolicy<QResourceManager>::WriteLocker writeLock(this);
366 // Test that the handle hasn't been set (in the meantime between the read unlock and the write lock)
367 Handle &handleToSet = m_keyToHandleMap[id];
368 if (handleToSet.isNull()) {
369 handleToSet = Allocator::allocateResource();
370 }
371 return handleToSet;
372 }
373 return handle;
374 }
375
376 Handle lookupHandle(const KeyType &id)
377 {
378 typename LockingPolicy<QResourceManager>::ReadLocker lock(this);
379 return m_keyToHandleMap.value(id);
380 }
381
382 ValueType *lookupResource(const KeyType &id)
383 {
384 ValueType* ret = nullptr;
385 {
386 typename LockingPolicy<QResourceManager>::ReadLocker lock(this);
387 Handle handle = m_keyToHandleMap.value(id);
388 if (!handle.isNull())
389 ret = Allocator::data(handle);
390 }
391 return ret;
392 }
393
394 ValueType *getOrCreateResource(const KeyType &id)
395 {
396 const Handle handle = getOrAcquireHandle(id);
397 return handle.operator->();
398 }
399
400 void releaseResource(const KeyType &id)
401 {
402 typename LockingPolicy<QResourceManager>::WriteLocker lock(this);
403 Handle handle = m_keyToHandleMap.take(id);
404 if (!handle.isNull())
405 Allocator::releaseResource(handle);
406 }
407
408 // Releases all resources referenced by a key
409 // Resources allocated manually with just a handle aren't releases
410 void releaseAllResources()
411 {
412 typename LockingPolicy<QResourceManager>::WriteLocker lock(this);
413 // Make a copy as releaseResource removes the entry in m_activeHanldes
414 const std::vector<Handle> activeHandles = Allocator::activeHandles();
415 for (const Handle &h : activeHandles)
416 Allocator::releaseResource(h);
417 // Clear Key to Handle Map
418 m_keyToHandleMap.clear();
419 }
420
421protected:
422 QHash<KeyType, Handle > m_keyToHandleMap;
423
424private:
425 friend QDebug operator<< <>(QDebug dbg, const QResourceManager<ValueType, KeyType, LockingPolicy> &manager);
426};
427
428#ifndef QT_NO_DEBUG_STREAM
429template <typename ValueType, typename KeyType,
430 template <class> class LockingPolicy
431 >
432QDebug operator<<(QDebug dbg, const QResourceManager<ValueType, KeyType, LockingPolicy> &manager)
433{
434 QDebugStateSaver saver(dbg);
435 dbg << "Contains" << manager.count() << "items" << Qt::endl;
436
437 dbg << "Key to Handle Map:" << Qt::endl;
438 const auto end = manager.m_keyToHandleMap.cend();
439 for (auto it = manager.m_keyToHandleMap.cbegin(); it != end; ++it)
440 dbg << "QNodeId =" << it.key() << "Handle =" << it.value() << Qt::endl;
441
442// dbg << "Resources:" << Qt::endl;
443// dbg << manager.m_handleManager;
444 return dbg;
445}
446#endif
447
448}// Qt3D
449
450QT_END_NAMESPACE
451
452#if defined(Q_CC_MSVC)
453#pragma warning(default : 4189)
454#endif
455
456#endif // QT3DCORE_QABSTRACTRESOURCESMANAGER_H
457

source code of qt3d/src/core/resources/qresourcemanager_p.h