1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#pragma once
4
5#include "Reflection/BsIReflectable.h"
6#include "Utility/BsUUID.h"
7
8namespace bs
9{
10 /** @addtogroup Implementation
11 * @{
12 */
13
14 /** Data that is shared between all resource handles. */
15 struct BS_CORE_EXPORT ResourceHandleData
16 {
17 SPtr<Resource> mPtr;
18 UUID mUUID;
19 bool mIsCreated = false;
20 std::atomic<std::uint32_t> mRefCount{0};
21 };
22
23 /**
24 * Represents a handle to a resource. Handles are similar to a smart pointers, but they have two advantages:
25 * - When loading a resource asynchronously you can be immediately returned the handle that you may use throughout
26 * the engine. The handle will be made valid as soon as the resource is loaded.
27 * - Handles can be serialized and deserialized, therefore saving/restoring references to their original resource.
28 */
29 class BS_CORE_EXPORT ResourceHandleBase : public IReflectable
30 {
31 public:
32 /**
33 * Checks if the resource is loaded. Until resource is loaded this handle is invalid and you may not get the
34 * internal resource from it.
35 *
36 * @param[in] checkDependencies If true, and if resource has any dependencies, this method will also check if
37 * they are loaded.
38 */
39 bool isLoaded(bool checkDependencies = true) const;
40
41 /**
42 * Blocks the current thread until the resource is fully loaded.
43 *
44 * @note Careful not to call this on the thread that does the loading.
45 */
46 void blockUntilLoaded(bool waitForDependencies = true) const;
47
48 /**
49 * Releases an internal reference to this resource held by the resources system, if there is one.
50 *
51 * @see Resources::release(ResourceHandleBase&)
52 */
53 void release();
54
55 /** Returns the UUID of the resource the handle is referring to. */
56 const UUID& getUUID() const { return mData != nullptr ? mData->mUUID : UUID::EMPTY; }
57
58 public: // ***** INTERNAL ******
59 /** @name Internal
60 * @{
61 */
62
63 /** Gets the handle data. For internal use only. */
64 const SPtr<ResourceHandleData>& getHandleData() const { return mData; }
65
66 /** @} */
67 protected:
68 /** Destroys the resource the handle is pointing to. */
69 void destroy();
70
71 /**
72 * Sets the created flag to true and assigns the resource pointer. Called by the constructors, or if you
73 * constructed just using a UUID, then you need to call this manually before you can access the resource from
74 * this handle.
75 *
76 * @note
77 * This is needed because two part construction is required due to multithreaded nature of resource loading.
78 * @note
79 * Internal method.
80 */
81 void setHandleData(const SPtr<Resource>& ptr, const UUID& uuid);
82
83 /**
84 * Clears the created flag and the resource pointer, making the handle invalid until the resource is loaded again
85 * and assigned through setHandleData().
86 */
87 void clearHandleData();
88
89 /** Increments the reference count of the handle. Only to be used by Resources for keeping internal references. */
90 void addInternalRef();
91
92 /** Decrements the reference count of the handle. Only to be used by Resources for keeping internal references. */
93 void removeInternalRef();
94
95 /**
96 * Notification sent by the resource system when the resource is done with the loading process. This will trigger
97 * even if the load fails.
98 */
99 void notifyLoadComplete();
100
101 /**
102 * @note
103 * All handles to the same source must share this same handle data. Otherwise things like counting number of
104 * references or replacing pointed to resource become impossible without additional logic. */
105 SPtr<ResourceHandleData> mData;
106
107 private:
108 friend class Resources;
109
110 static Signal mResourceCreatedCondition;
111 static Mutex mResourceCreatedMutex;
112
113 protected:
114 void throwIfNotLoaded() const;
115 };
116
117 /**
118 * @copydoc ResourceHandleBase
119 *
120 * Handles differences in reference counting depending if the handle is normal or weak.
121 */
122 template <bool WeakHandle>
123 class BS_CORE_EXPORT TResourceHandleBase : public ResourceHandleBase
124 { };
125
126 /** Specialization of TResourceHandleBase for weak handles. Weak handles do no reference counting. */
127 template<>
128 class BS_CORE_EXPORT TResourceHandleBase<true> : public ResourceHandleBase
129 {
130 protected:
131 void addRef() { };
132 void releaseRef() { };
133
134 /************************************************************************/
135 /* RTTI */
136 /************************************************************************/
137 public:
138 friend class WeakResourceHandleRTTI;
139 static RTTITypeBase* getRTTIStatic();
140 RTTITypeBase* getRTTI() const override;
141 };
142
143 /** Specialization of TResourceHandleBase for normal (non-weak) handles. */
144 template<>
145 class BS_CORE_EXPORT TResourceHandleBase<false> : public ResourceHandleBase
146 {
147 protected:
148 void addRef()
149 {
150 if (mData)
151 mData->mRefCount.fetch_add(1, std::memory_order_relaxed);
152 };
153
154 void releaseRef()
155 {
156 if (mData)
157 {
158 std::uint32_t refCount = mData->mRefCount.fetch_sub(1, std::memory_order_release);
159
160 if (refCount == 1)
161 {
162 std::atomic_thread_fence(std::memory_order_acquire);
163 destroy();
164 }
165 }
166 };
167
168 /************************************************************************/
169 /* RTTI */
170 /************************************************************************/
171 public:
172 friend class WeakResourceHandleRTTI;
173 friend class ResourceHandleRTTI;
174 static RTTITypeBase* getRTTIStatic();
175 RTTITypeBase* getRTTI() const override;
176 };
177
178 /** @copydoc ResourceHandleBase */
179 template <typename T, bool WeakHandle>
180 class TResourceHandle : public TResourceHandleBase<WeakHandle>
181 {
182 public:
183 TResourceHandle() = default;
184
185 TResourceHandle(std::nullptr_t) { }
186
187 /** Copy constructor. */
188 TResourceHandle(const TResourceHandle& other)
189 {
190 this->mData = other.getHandleData();
191 this->addRef();
192 }
193
194 /** Move constructor. */
195 TResourceHandle(TResourceHandle&& other) = default;
196
197 ~TResourceHandle()
198 {
199 this->releaseRef();
200 }
201
202 /** Converts a specific handle to generic Resource handle. */
203 operator TResourceHandle<Resource, WeakHandle>() const
204 {
205 TResourceHandle<Resource, WeakHandle> handle;
206 handle.setHandleData(this->getHandleData());
207
208 return handle;
209 }
210
211 /**
212 * Returns internal resource pointer.
213 *
214 * @note Throws exception if handle is invalid.
215 */
216 T* operator->() const { return get(); }
217
218 /**
219 * Returns internal resource pointer and dereferences it.
220 *
221 * @note Throws exception if handle is invalid.
222 */
223 T& operator*() const { return *get(); }
224
225 /** Clears the handle making it invalid and releases any references held to the resource. */
226 TResourceHandle<T, WeakHandle>& operator=(std::nullptr_t ptr)
227 {
228 this->releaseRef();
229 this->mData = nullptr;
230
231 return *this;
232 }
233
234 /** Copy assignment. */
235 TResourceHandle<T, WeakHandle>& operator=(const TResourceHandle<T, WeakHandle>& rhs)
236 {
237 setHandleData(rhs.getHandleData());
238 return *this;
239 }
240
241 /** Move assignment. */
242 TResourceHandle& operator=(TResourceHandle&& other)
243 {
244 if(this == &other)
245 return *this;
246
247 this->releaseRef();
248 this->mData = std::exchange(other.mData, nullptr);
249
250 return *this;
251 }
252
253 template<class _Ty>
254 struct Bool_struct
255 {
256 int _Member;
257 };
258
259 /**
260 * Allows direct conversion of handle to bool.
261 *
262 * @note This is needed because we can't directly convert to bool since then we can assign pointer to bool and
263 * that's weird.
264 */
265 operator int Bool_struct<T>::*() const
266 {
267 return ((this->mData != nullptr && !this->mData->mUUID.empty()) ? &Bool_struct<T>::_Member : 0);
268 }
269
270 /**
271 * Returns internal resource pointer and dereferences it.
272 *
273 * @note Throws exception if handle is invalid.
274 */
275 T* get() const
276 {
277 this->throwIfNotLoaded();
278
279 return reinterpret_cast<T*>(this->mData->mPtr.get());
280 }
281
282 /**
283 * Returns the internal shared pointer to the resource.
284 *
285 * @note Throws exception if handle is invalid.
286 */
287 SPtr<T> getInternalPtr() const
288 {
289 this->throwIfNotLoaded();
290
291 return std::static_pointer_cast<T>(this->mData->mPtr);
292 }
293
294 /** Converts a handle into a weak handle. */
295 TResourceHandle<T, true> getWeak() const
296 {
297 TResourceHandle<T, true> handle;
298 handle.setHandleData(this->getHandleData());
299
300 return handle;
301 }
302
303 protected:
304 friend Resources;
305 template<class _T, bool _Weak>
306 friend class TResourceHandle;
307 template<class _Ty1, class _Ty2, bool _Weak2, bool _Weak1>
308 friend TResourceHandle<_Ty1, _Weak1> static_resource_cast(const TResourceHandle<_Ty2, _Weak2>& other);
309
310 /**
311 * Constructs a new valid handle for the provided resource with the provided UUID.
312 *
313 * @note Handle will take ownership of the provided resource pointer, so make sure you don't delete it elsewhere.
314 */
315 explicit TResourceHandle(T* ptr, const UUID& uuid)
316 :TResourceHandleBase<WeakHandle>()
317 {
318 this->mData = bs_shared_ptr_new<ResourceHandleData>();
319 this->addRef();
320
321 this->setHandleData(SPtr<Resource>(ptr), uuid);
322 this->mIsCreated = true;
323 }
324
325 /**
326 * Constructs an invalid handle with the specified UUID. You must call setHandleData() with the actual resource
327 * pointer to make the handle valid.
328 */
329 TResourceHandle(const UUID& uuid)
330 {
331 this->mData = bs_shared_ptr_new<ResourceHandleData>();
332 this->mData->mUUID = uuid;
333
334 this->addRef();
335 }
336
337 /** Constructs a new valid handle for the provided resource with the provided UUID. */
338 TResourceHandle(const SPtr<T> ptr, const UUID& uuid)
339 {
340 this->mData = bs_shared_ptr_new<ResourceHandleData>();
341 this->addRef();
342
343 this->setHandleData(ptr, uuid);
344 this->mData->mIsCreated = true;
345 }
346
347 /** Replaces the internal handle data pointer, effectively transforming the handle into a different handle. */
348 void setHandleData(const SPtr<ResourceHandleData>& data)
349 {
350 this->releaseRef();
351 this->mData = data;
352 this->addRef();
353 }
354
355 /** Converts a weak handle into a normal handle. */
356 TResourceHandle<T, false> lock() const
357 {
358 TResourceHandle<Resource, false> handle;
359 handle.setHandleData(this->getHandleData());
360
361 return handle;
362 }
363
364 using ResourceHandleBase::setHandleData;
365 };
366
367 /** Checks if two handles point to the same resource. */
368 template<class _Ty1, bool _Weak1, class _Ty2, bool _Weak2>
369 bool operator==(const TResourceHandle<_Ty1, _Weak1>& _Left, const TResourceHandle<_Ty2, _Weak2>& _Right)
370 {
371 if(_Left.getHandleData() != nullptr && _Right.getHandleData() != nullptr)
372 return _Left.getHandleData()->mPtr == _Right.getHandleData()->mPtr;
373
374 return _Left.getHandleData() == _Right.getHandleData();
375 }
376
377 /** Checks if a handle is null. */
378 template<class _Ty1, bool _Weak1, class _Ty2, bool _Weak2>
379 bool operator==(const TResourceHandle<_Ty1, _Weak1>& _Left, std::nullptr_t _Right)
380 {
381 return _Left.getHandleData() == nullptr || _Left.getHandleData()->mUUID.empty();
382 }
383
384 template<class _Ty1, bool _Weak1, class _Ty2, bool _Weak2>
385 bool operator!=(const TResourceHandle<_Ty1, _Weak1>& _Left, const TResourceHandle<_Ty2, _Weak2>& _Right)
386 {
387 return (!(_Left == _Right));
388 }
389
390 /** @} */
391
392 /** @addtogroup Resources
393 * @{
394 */
395
396 /** @copydoc ResourceHandleBase */
397 template <typename T>
398 using ResourceHandle = TResourceHandle<T, false>;
399
400 /**
401 * @copydoc ResourceHandleBase
402 *
403 * Weak handles don't prevent the resource from being unloaded.
404 */
405 template <typename T>
406 using WeakResourceHandle = TResourceHandle<T, true>;
407
408 /** Casts one resource handle to another. */
409 template<class _Ty1, class _Ty2, bool _Weak2, bool _Weak1 = false>
410 TResourceHandle<_Ty1, _Weak1> static_resource_cast(const TResourceHandle<_Ty2, _Weak2>& other)
411 {
412 TResourceHandle<_Ty1, _Weak1> handle;
413 handle.setHandleData(other.getHandleData());
414
415 return handle;
416 }
417
418 /** @} */
419}
420