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