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 | |
8 | namespace 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 | |