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