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 "BsCorePrerequisites.h"
6#include "Utility/BsModule.h"
7
8namespace bs
9{
10 /** @addtogroup Resources
11 * @{
12 */
13
14 /** Flags that can be used to control resource loading. */
15 enum class BS_SCRIPT_EXPORT(m:Resources) ResourceLoadFlag
16 {
17 /** No flags. */
18 None = 0,
19 /** If enabled all resources referenced by the root resource will be loaded as well. */
20 LoadDependencies = 1 << 0,
21 /**
22 * If enabled the resource system will keep an internal reference to the resource so it doesn't get destroyed when
23 * it goes out of scope. You can call Resources::release() to release the internal reference. Each call to load will
24 * create a new internal reference and therefore must be followed by the same number of release calls. If
25 * dependencies are being loaded, they will not have internal references created regardless of this parameter.
26 */
27 KeepInternalRef = 1 << 1,
28 /**
29 * Determines if the loaded resource keeps original data loaded. Sometime resources will process loaded data
30 * and discard the original (e.g. uncompressing audio on load). This flag can prevent the resource from discarding
31 * the original data. The original data might be required for saving the resource (via Resources::save), but will
32 * use up extra memory. Normally you want to keep this enabled if you plan on saving the resource to disk.
33 */
34 KeepSourceData = 1 << 2,
35 /** Default set of flags used for resource loading. */
36 Default = LoadDependencies | KeepInternalRef
37 };
38
39 typedef Flags<ResourceLoadFlag> ResourceLoadFlags;
40 BS_FLAGS_OPERATORS(ResourceLoadFlag);
41
42 /**
43 * Manager for dealing with all engine resources. It allows you to save new resources and load existing ones.
44 *
45 * @note Sim thread only.
46 */
47 class BS_CORE_EXPORT BS_SCRIPT_EXPORT(m:Resources,api:bsf) Resources : public Module<Resources>
48 {
49 /** Information about a loaded resource. */
50 struct LoadedResourceData
51 {
52 LoadedResourceData() = default;
53 LoadedResourceData(const WeakResourceHandle<Resource>& resource, UINT32 size)
54 :resource(resource), size(size)
55 { }
56
57 WeakResourceHandle<Resource> resource;
58 UINT32 numInternalRefs = 0;
59 UINT32 size = 0;
60 };
61
62 /** Information about a resource that's currently being loaded. */
63 struct ResourceLoadData
64 {
65 ResourceLoadData(const WeakResourceHandle<Resource>& resource, UINT32 numDependencies, UINT32 size)
66 :resData(resource, size), remainingDependencies(numDependencies)
67 { }
68
69 LoadedResourceData resData;
70 SPtr<Resource> loadedData;
71 UINT32 remainingDependencies;
72 Vector<HResource> dependencies;
73 bool notifyImmediately;
74
75 // Progress reporting
76 UINT32 dependencySize = 0;
77 UINT32 dependencyLoadedAmount = 0;
78 std::atomic<float> progress;
79 };
80
81 /** Information about an issued resource load. */
82 struct LoadInfo
83 {
84 enum State { Loading, Failed, AlreadyInProgress, AlreadyLoaded };
85
86 HResource resource;
87 UINT32 size;
88 State state;
89 };
90
91 public:
92 Resources();
93 ~Resources();
94
95 /**
96 * Loads the resource from a given path. Returns an empty handle if resource can't be loaded. Resource is loaded
97 * synchronously.
98 *
99 * @param[in] filePath File path to the resource to load. This can be absolute or relative to the working
100 * folder.
101 * @param[in] loadFlags Flags used to control the load process.
102 *
103 * @see release(ResourceHandleBase&), unloadAllUnused()
104 */
105 BS_SCRIPT_EXPORT()
106 BS_NORREF HResource load(const Path& filePath, ResourceLoadFlags loadFlags = ResourceLoadFlag::Default);
107
108 /** @copydoc load(const Path&, ResourceLoadFlags) */
109 template <class T>
110 ResourceHandle<T> load(const Path& filePath, ResourceLoadFlags loadFlags = ResourceLoadFlag::Default)
111 {
112 return static_resource_cast<T>(load(filePath, loadFlags));
113 }
114
115 /**
116 * Loads the resource for the provided weak resource handle, or returns a loaded resource if already loaded.
117 *
118 * @see load(const Path&, ResourceLoadFlags)
119 */
120 HResource load(const WeakResourceHandle<Resource>& handle, ResourceLoadFlags loadFlags = ResourceLoadFlag::Default);
121
122 /** @copydoc load(const WeakResourceHandle<Resource>&, ResourceLoadFlags) */
123 template <class T>
124 ResourceHandle<T> load(const WeakResourceHandle<T>& handle, ResourceLoadFlags loadFlags = ResourceLoadFlag::Default)
125 {
126 return static_resource_cast<T>(load((const WeakResourceHandle<Resource>&)handle, loadFlags));
127 }
128
129 /**
130 * Loads the resource asynchronously. Initially returned resource handle will be invalid until resource loading is
131 * done. Use ResourceHandle<T>::isLoaded to check if resource has been loaded, or
132 * ResourceHandle<T>::blockUntilLoaded to wait until load completes.
133 *
134 * @param[in] filePath Full pathname of the file.
135 * @param[in] loadFlags Flags used to control the load process.
136 *
137 * @see load(const Path&, ResourceLoadFlags)
138 */
139 BS_SCRIPT_EXPORT()
140 HResource loadAsync(const Path& filePath, ResourceLoadFlags loadFlags = ResourceLoadFlag::Default);
141
142 /** @copydoc loadAsync */
143 template <class T>
144 ResourceHandle<T> loadAsync(const Path& filePath, ResourceLoadFlags loadFlags = ResourceLoadFlag::Default)
145 {
146 return static_resource_cast<T>(loadAsync(filePath, loadFlags));
147 }
148
149 /**
150 * Loads the resource with the given UUID. Returns an empty handle if resource can't be loaded.
151 *
152 * @param[in] uuid UUID of the resource to load.
153 * @param[in] async If true resource will be loaded asynchronously. Handle to non-loaded resource will be
154 * returned immediately while loading will continue in the background.
155 * @param[in] loadFlags Flags used to control the load process.
156 *
157 * @see load(const Path&, bool)
158 */
159 BS_SCRIPT_EXPORT()
160 HResource loadFromUUID(const UUID& uuid, bool async = false, ResourceLoadFlags loadFlags = ResourceLoadFlag::Default);
161
162 /**
163 * Releases an internal reference to the resource held by the resources system. This allows the resource to be
164 * unloaded when it goes out of scope, if the resource was loaded with @p keepInternalReference parameter.
165 *
166 * Alternatively you can also skip manually calling release() and call unloadAllUnused() which will unload all
167 * resources that do not have any external references, but you lose the fine grained control of what will be
168 * unloaded.
169 *
170 * @param[in] resource Handle of the resource to release.
171 */
172 BS_SCRIPT_EXPORT()
173 void release(const HResource& resource) { release((ResourceHandleBase&)resource); }
174
175 /** @copydoc release(const HResource&) */
176 void release(ResourceHandleBase& resource);
177
178 /**
179 * Finds all resources that aren't being referenced outside of the resources system and unloads them.
180 *
181 * @see release(const HResource&)
182 */
183 BS_SCRIPT_EXPORT()
184 void unloadAllUnused();
185
186 /** Forces unload of all resources, whether they are being used or not. */
187 BS_SCRIPT_EXPORT()
188 void unloadAll();
189
190 /**
191 * Saves the resource at the specified location.
192 *
193 * @param[in] resource Handle to the resource.
194 * @param[in] filePath Full pathname of the file to save as.
195 * @param[in] overwrite If true, any existing resource at the specified location will be overwritten.
196 * @param[in] compress Should the resource be compressed before saving. Some resources have data that is
197 * already compressed and this option will be ignored for such resources.
198 *
199 * @note
200 * If the resource is used on the GPU and you are in some way modifying it from the core thread, make sure all
201 * core thread commands are submitted and executed before you call this method. Otherwise an obsolete version of
202 * the resource might get saved.
203 * @note
204 * If saving a core thread resource this is a potentially very slow operation as we must wait on the core thread
205 * and the GPU in order to read the resource.
206 * @note
207 * Thread safe if you guarantee the resource isn't being written to from another thread.
208 */
209 BS_SCRIPT_EXPORT()
210 void save(BS_NORREF const HResource& resource, const Path& filePath, bool overwrite, bool compress = false);
211
212 /**
213 * Saves an existing resource to its previous location.
214 *
215 * @param[in] resource Handle to the resource.
216 * @param[in] compress Should the resource be compressed before saving. Some resources have data that is
217 * already compressed and this option will be ignored for such resources.
218 *
219 * @note
220 * If the resource is used on the GPU and you are in some way modifying it from the core thread, make sure all
221 * core thread commands are submitted and executed before you call this method. Otherwise an obsolete version of
222 * the resource might get saved.
223 * @note
224 * If saving a core thread resource this is a potentially very slow operation as we must wait on the core thread
225 * and the GPU in order to read the resource.
226 * @note
227 * Thread safe if you guarantee the resource isn't being written to from another thread.
228 */
229 BS_SCRIPT_EXPORT()
230 void save(BS_NORREF const HResource& resource, bool compress = false);
231
232 /**
233 * Updates an existing resource handle with a new resource. Caller must ensure that new resource type matches the
234 * original resource type.
235 */
236 void update(HResource& handle, const SPtr<Resource>& resource);
237
238 /**
239 * Returns a list of dependencies from the resources at the specified path. Resource will not be loaded or parsed,
240 * but instead the saved list of dependencies will be read from the file and returned.
241 *
242 * @param[in] filePath Full path to the resource to get dependencies for.
243 * @return List of dependencies represented as UUIDs.
244 */
245 BS_SCRIPT_EXPORT()
246 Vector<UUID> getDependencies(const Path& filePath);
247
248 /**
249 * Checks is the resource with the specified UUID loaded.
250 *
251 * @param[in] uuid UUID of the resource to check.
252 * @param[in] checkInProgress Should this method also check resources that are in progress of being
253 * asynchronously loaded.
254 * @return True if loaded or loading in progress, false otherwise.
255 */
256 BS_SCRIPT_EXPORT()
257 bool isLoaded(const UUID& uuid, bool checkInProgress = true);
258
259 /**
260 * Returns the loading progress of a resource that's being asynchronously loaded.
261 *
262 * @param[in] resource Resource whose load progress to check.
263 * @param[in] includeDependencies If false the progress will reflect the load progress only for this
264 * inidividual resource. If true the progress will reflect load progress
265 * of this resource and all of its dependencies.
266 * @return Load progress in range [0, 1].
267 */
268 BS_SCRIPT_EXPORT()
269 float getLoadProgress(const HResource& resource, bool includeDependencies = true);
270
271 /**
272 *Allows you to set a resource manifest containing UUID <-> file path mapping that is used when resolving
273 * resource references.
274 *
275 * @note
276 * If you want objects that reference resources (using ResourceHandles) to be able to find that resource even after
277 * application restart, then you must save the resource manifest before closing the application and restore it
278 * upon startup. Otherwise resources will be assigned brand new UUIDs and references will be broken.
279 */
280 BS_SCRIPT_EXPORT()
281 void registerResourceManifest(const SPtr<ResourceManifest>& manifest);
282
283 /** Unregisters a resource manifest previously registered with registerResourceManifest(). */
284 BS_SCRIPT_EXPORT()
285 void unregisterResourceManifest(const SPtr<ResourceManifest>& manifest);
286
287 /**
288 * Allows you to retrieve resource manifest containing UUID <-> file path mapping that is used when resolving
289 * resource references.
290 *
291 * @note
292 * Resources module internally holds a "Default" manifest that it automatically updated whenever a resource is saved.
293 *
294 * @see registerResourceManifest
295 */
296 BS_SCRIPT_EXPORT()
297 SPtr<ResourceManifest> getResourceManifest(const String& name) const;
298
299 /** Attempts to retrieve file path from the provided UUID. Returns true if successful, false otherwise. */
300 BS_SCRIPT_EXPORT()
301 bool getFilePathFromUUID(const UUID& uuid, Path& filePath) const;
302
303 /** Attempts to retrieve UUID from the provided file path. Returns true if successful, false otherwise. */
304 BS_SCRIPT_EXPORT()
305 bool getUUIDFromFilePath(const Path& path, UUID& uuid) const;
306
307 /**
308 * Called when the resource has been successfully loaded.
309 *
310 * @note
311 * It is undefined from which thread this will get called from. Most definitely not the sim thread if resource was
312 * being loaded asynchronously.
313 */
314 BS_SCRIPT_EXPORT()
315 Event<void(BS_NORREF const HResource&)> onResourceLoaded;
316
317 /**
318 * Called when the resource has been destroyed. Provides UUID of the destroyed resource.
319 *
320 * @note It is undefined from which thread this will get called from.
321 */
322 BS_SCRIPT_EXPORT()
323 Event<void(const UUID&)> onResourceDestroyed;
324
325 /**
326 * Called when the internal resource the handle is pointing to has changed.
327 *
328 * @note It is undefined from which thread this will get called from.
329 */
330 BS_SCRIPT_EXPORT()
331 Event<void(BS_NORREF const HResource&)> onResourceModified;
332
333 public: // ***** INTERNAL ******
334 /** @name Internal
335 * @{
336 */
337
338 /**
339 * Creates a new resource handle from a resource pointer.
340 *
341 * @note Internal method used primarily be resource factory methods.
342 */
343 HResource _createResourceHandle(const SPtr<Resource>& obj);
344
345 /**
346 * Creates a new resource handle from a resource pointer, with a user defined UUID.
347 *
348 * @note Internal method used primarily be resource factory methods.
349 */
350 HResource _createResourceHandle(const SPtr<Resource>& obj, const UUID& UUID);
351
352 /** Returns an existing handle for the specified UUID if one exists, or creates a new one. */
353 HResource _getResourceHandle(const UUID& uuid);
354
355 /**
356 * Same as save() except it saves the resource without registering it in the default manifest, requiring a handle,
357 * or checking for overwrite.
358 */
359 void _save(const SPtr<Resource>& resource, const Path& filePath, bool compress);
360
361 /** @} */
362 private:
363 friend class ResourceHandleBase;
364
365 /**
366 * Starts resource loading or returns an already loaded resource. Both UUID and filePath must match the same
367 * resource, although you may provide an empty path in which case the resource will be retrieved from memory if its
368 * currently loaded.
369 */
370 LoadInfo loadInternal(const UUID& UUID, const Path& filePath, bool synchronous, ResourceLoadFlags loadFlags);
371
372 /** Performs actually reading and deserializing of the resource file. Called from various worker threads. */
373 SPtr<Resource> loadFromDiskAndDeserialize(const Path& filePath, bool loadWithSaveData, std::atomic<float>& progress);
374
375 /** Triggered when individual resource has finished loading. */
376 void loadComplete(HResource& resource, bool notifyProgress);
377
378 /** Callback triggered when the task manager is ready to process the loading task. */
379 void loadCallback(const Path& filePath, HResource& resource, bool loadWithSaveData);
380
381 /** Destroys a resource, freeing its memory. */
382 void destroy(ResourceHandleBase& resource);
383
384 private:
385 Vector<SPtr<ResourceManifest>> mResourceManifests;
386 SPtr<ResourceManifest> mDefaultResourceManifest;
387
388 Mutex mInProgressResourcesMutex;
389 Mutex mLoadedResourceMutex;
390 Mutex mDefaultManifestMutex;
391 RecursiveMutex mDestroyMutex;
392
393 UnorderedMap<UUID, WeakResourceHandle<Resource>> mHandles;
394 UnorderedMap<UUID, LoadedResourceData> mLoadedResources;
395 UnorderedMap<UUID, ResourceLoadData*> mInProgressResources; // Resources that are being asynchronously loaded
396 UnorderedMap<UUID, Vector<ResourceLoadData*>> mDependantLoads; // Allows dependency to be notified when a dependant is loaded
397 };
398
399 /** Provides easier access to Resources manager. */
400 BS_CORE_EXPORT Resources& gResources();
401
402 /** @} */
403}