00001 /*************************************************************************** 00002 * * 00003 * (c) Art Tevs, MPI Informatik Saarbruecken * 00004 * mailto: <tevs@mpi-sb.mpg.de> * 00005 * * 00006 * This program is free software; you can redistribute it and/or modify * 00007 * it under the terms of the GNU General Public License as published by * 00008 * the Free Software Foundation; either version 2 of the License, or * 00009 * (at your option) any later version. * 00010 * * 00011 ***************************************************************************/ 00012 00013 00014 #ifndef _NR_RESOURCE_MANAGER_H_ 00015 #define _NR_RESOURCE_MANAGER_H_ 00016 00017 00018 //---------------------------------------------------------------------------------- 00019 // Includes 00020 //---------------------------------------------------------------------------------- 00021 #include "Prerequisities.h" 00022 #include "ResourceSystem.h" 00023 #include "ScriptEngine.h" 00024 00025 namespace nrEngine { 00026 00027 //! General pointer/handle based resource management system 00028 /** 00029 * This class is a manger for every kind of resource used in our games. 00030 * Textures, sounds, meshes, ... - all things that can be loaded from disk 00031 * are resources managed by this module. You can derive your own classes 00032 * to write own storing algorithms for different kind of resources. This class 00033 * will use standard implementation and will try to be as efficient as possible. 00034 * This manager can index resources, look them up, load and destroy them, and 00035 * temporaly unload resources from memory to use only allowd size of memory. 00036 * 00037 * We will use a priority system to determine which resources can be unloaded 00038 * and for two resources of the same priority the most used will stay in memory. 00039 * 00040 * This code and idea was get from Game Programming Gems 4. <a href="http://www.ogre3d.org"> 00041 * Ogre3D-Library</a> was also used to get an idea how to get the resource manager 00042 * working without using a whole library behind. 00043 * 00044 * 00045 * <b>-</b> Meaning of resource groups: 00046 * @par - Each resource can exists in a group. 00047 * - Each group has got it's unique names. 00048 * - You can access to whole groups either to single resources. 00049 * - E.g: you can split your level in patches and each resource in such a patch is 00050 * in patch's resource group. So if the player go to far away from such a patch, 00051 * it can be disabled or unloaded from the memory. You have now just to call one 00052 * unload function for the whole group. 00053 * - Groups must not be disjoint, so you can have same resource in different groups. 00054 * 00055 * \ingroup resource 00056 **/ 00057 class _NRExport ResourceManager{ 00058 public: 00059 00060 /** 00061 * Release all used memory and also mark all loaded resources to unload. After 00062 * you calling the destructor of ResourceManager class all created resources 00063 * and loaders will be removed from the memory. 00064 **/ 00065 virtual ~ResourceManager(); 00066 00067 #if 0 00068 /** 00069 * Set amount of memory that can be used by the manager. Set this value to 80-90% 00070 * of your physical memory if you do not have any virtual memory in your system 00071 * available. The manager will try to stay in the budget you given for him. 00072 * Resources that are least used and has got lowest priority will be unloaded 00073 * to free the memory to stay in the given budget of memory. 00074 * 00075 * @param bytes Count of bytes which can be used for resource storing 00076 * @return either OK or error code. 00077 * 00078 * @note You can set this value to ca 80-90% of your memory available. 00079 * Before resource manager unload least used resources it will allocate 00080 * memory for the new resource to check how much it needs. So we still need 00081 * some free memory for this temporary allocation. So 80-90% are good 00082 * values for memory budget. 00083 * If you want to be very precisious you can set this value to memory 00084 * size minus biggest resource size. Which will use maximal available 00085 * memory space. 00086 **/ 00087 Result setMemoryBudget(size_t bytes); 00088 00089 00090 /** 00091 * Get the memory budget resource manager have got. 00092 **/ 00093 size_t getMemoryBudget() const; 00094 00095 00096 /** 00097 * Returns the usage of memory in bytes. 00098 **/ 00099 size_t getMemoryUsage() const; 00100 #endif 00101 00102 /** 00103 * Here you can register any loader by the manager. Loader are used to load resources 00104 * from files/memory and to store resources in the memory. By registration of 00105 * a loader you have to specify his unique name. This name will be used to access 00106 * to stored loader (for example to remove it). 00107 * 00108 * @param name Unique loader name 00109 * @param loader Smart pointer containing the loader 00110 * 00111 * @return either OK or error code: 00112 * - RES_LOADER_ALREADY_EXISTS 00113 * - BAD_PARAMETERS 00114 * 00115 * @note If there is two loaders which do support loading of file of the same filetype, 00116 * so the behavior of getting appropriate loader is undefined. 00117 **/ 00118 Result registerLoader (const std::string& name, ResourceLoader loader); 00119 00120 00121 /** 00122 * Removes the loader from the loader list. All bounded filetypes 00123 * registrations will also be removed. So after you calling this you have 00124 * to check whenever you still able to load filetypes you want to use. 00125 * 00126 * @param name Unique name of the loader 00127 * @return either OK or error code: 00128 * - RES_LOADER_NOT_REGISTERED 00129 * - RES_ERROR 00130 **/ 00131 Result removeLoader (const std::string& name); 00132 00133 00134 /** 00135 * Return the loader by given filetype. If there is no loader which can load 00136 * such a filetype NULL will be returned 00137 * @param fileType File type for which one the loader should be found 00138 **/ 00139 ResourceLoader getLoaderByFile (const std::string& fileType); 00140 00141 00142 /** 00143 * Get a loader that support creating of resource instances of the given resource type 00144 * @param resType Unique name of the resource type 00145 * @return either NULL or resource loader 00146 **/ 00147 ResourceLoader getLoaderByResource(const std::string& resType); 00148 00149 00150 /** 00151 * Return the loader by given name. If the loader with this name does not 00152 * exists, so NULL will be given back 00153 **/ 00154 ResourceLoader getLoader (const std::string& name); 00155 00156 00157 /** 00158 * Create a resource object of a certain type. An according resource loader has 00159 * to be registered before to provide manager with creating function. 00160 * If no loader was registered NULL will be returned. 00161 * 00162 * After you created a resource you will still not be able to use it. You have 00163 * to load a resource from a file. You have also to load it, because we are using 00164 * concept of empty resources. So loader has to get access on the resource to 00165 * initialize it with empty data. 00166 * 00167 * @param name Unique name of that resource. The name must be specified !!! 00168 * @param group Name of the group to which this resource belongs. 00169 * @param resourceType Unique type name of the resource type 00170 * @param params Here you can specify parameter which will be send to the loader/creator 00171 * so that he can setup resource parameters. Default is NULL, so no parameters. 00172 **/ 00173 IResourcePtr createResource (const std::string& name, 00174 const std::string& group, 00175 const std::string& resourceType, 00176 PropertyList* params = NULL); 00177 00178 /** 00179 * Load a resource from a file. For loading of a resource the registered 00180 * loader will be used. The filetype is defined by the last 00181 * characters of the file name. If we can not find this characters 00182 * so the manual loader will be used if such one is specified. Manual loader 00183 * will also be used if it is specified and filetype is detected. 00184 * You can assign your resource to special group. The group names 00185 * has to be unique. 00186 * 00187 * If such a resource could not load, NULL will be returned. 00188 * 00189 * If you do not specify any resource type, so the loader which has to be used 00190 * will be detected by the file name. If no such found error will be given back. 00191 * 00192 * @param name Unique name fo the resource 00193 * @param group Group name to which this resource should be assigned 00194 * @param resourceType Unique type name of the resource 00195 * @param fileName File name of the file to be loaded 00196 * @param params Here you can specify parameter which will be send to the loader/creator 00197 * so that he can setup resource parameters. Default is NULL, so no parameters. 00198 * @param manualLoader Loader which should be used if you want to overload 00199 * all registered loader. 00200 * 00201 **/ 00202 IResourcePtr loadResource (const std::string& name, 00203 const std::string& group, 00204 const std::string& fileName, 00205 const std::string& resourceType = std::string(), 00206 PropertyList* params = NULL, 00207 ResourceLoader manualLoader = ResourceLoader()); 00208 00209 #if 0 00210 /** 00211 * This function will add a given resource to the resource management system. 00212 * From now one resource management get the rights for unloading/reloading and 00213 * removing resources from the memory. 00214 * 00215 * @param res Resource pointer to the resource. (Be careful this is not a smart pointer) 00216 * @param name Unique name of the resource that should be used in the database 00217 * @param group Name of the group to which the resource belongs. If no name specified, 00218 * then use name stored in the resource. 00219 * @return either OK or error code 00220 * 00221 * @note The resource should already know which loader does loading 00222 * of it from files. So database will not assign any loader to 00223 * that resource. You should do it before calling this method. 00224 **/ 00225 Result add(IResource* res, const std::string& name, const std::string& group = ""); 00226 #endif 00227 00228 /** 00229 * This method is used to lock the resource with the given name for using. 00230 * Locking of pure resource means here, that we want now to access to real resource 00231 * either to empty resource. Because of our transparent functionality of changing 00232 * between real and empty resource (if real resource is unloaded), we must have a possibility 00233 * to access to real resources bypassing this changing mechanism. You will need this for 00234 * example if you want to change some properties of a resource (for example texture flags). 00235 * If your resource is currently unloaded from the memory and you will try to change properties, 00236 * so there would be now effect because settings get changed in empty resource which would 00237 * not change anything (Assumption: resources handles correctly). 00238 * 00239 * Assume the resource is not loaded and you want to access it. 00240 * <code> 00241 * resource->setPropertyOne(...); 00242 * </code> 00243 * Affect to empty resource, because the resource is unloaded.<br> 00244 * <code> 00245 * ResourceMgr.lockResource("resource");<br> 00246 * resource->setPropertyOne(...);<br> 00247 * ResourceMgr.unlockResource("resource");<br> 00248 * </code> 00249 * Affect to the resource you probably means. You will set the property of the real 00250 * resource and not the empty one. 00251 * 00252 * @param name Unique name of the resource 00253 * @return either OK or error code 00254 * 00255 * @note You have to decide whenever you want to lock your resource or just work as it is. It is safer 00256 * to do locking if you have something like write function and do not lock anything for read only functions. 00257 * e.g: 00258 * - ReadOnly by textures : getting of a ID, drawing, ... 00259 * - Write by texture: set new size, get name (it has also to be locked, because empty texture will return 00260 * a name according to empty texture), load, ... 00261 **/ 00262 virtual Result lockResource(const std::string& name); 00263 00264 /** 00265 * Overloaded function to allow locking through pointer directly 00266 * @param res Pointer to the resource 00267 **/ 00268 virtual Result lockResource(IResourcePtr& res); 00269 00270 /** 00271 * Overloaded function to allow locking through handle. 00272 * @param handle Unique handle of the resource 00273 **/ 00274 virtual Result lockResource(ResourceHandle& handle); 00275 00276 /** 00277 * This function is complement to the lockResource(...) methods. This will unlock the 00278 * real resource from using 00279 * @param name Unique name of the resource to be unlocked 00280 **/ 00281 virtual Result unlockResource(const std::string& name); 00282 00283 /** 00284 * Overloaded function to allow unlocking through pointer directly 00285 * @param res Pointer to the resource 00286 **/ 00287 virtual Result unlockResource(IResourcePtr& res); 00288 00289 /** 00290 * Overloaded function to allow unlocking through handle. 00291 * @param handle Unique handle of the resource 00292 **/ 00293 virtual Result unlockResource(ResourceHandle& handle); 00294 00295 /** 00296 * Unload the resource from the memory. 00297 * After this call all the resource pointers assigned within the resource 00298 * will point to an empty resource. 00299 * 00300 * @param name Unique name of the resource 00301 * @return either OK or error code: 00302 * - RES_NOT_FOUND 00303 * @see IResourceLoader 00304 **/ 00305 Result unload(const std::string& name); 00306 00307 //! @see unload(name) 00308 Result unload(IResourcePtr& res); 00309 00310 //! @see unload(name) 00311 Result unload(ResourceHandle& handle); 00312 00313 00314 /** 00315 * Reload the resource, so it will now contain it's real data. 00316 * Call this function if want to get your resource back after 00317 * it was unloaded. 00318 * 00319 * @param name Unique name of the resource 00320 * @return either OK or error code: 00321 * - RES_NOT_FOUND 00322 **/ 00323 Result reload(const std::string& name); 00324 00325 //! @see reload(name) 00326 Result reload(IResourcePtr& res); 00327 00328 //! @see reload(name) 00329 Result reload(ResourceHandle& handle); 00330 00331 00332 /** 00333 * This will remove the resource from the database. 00334 * After you removed the resource and would try to access to it through the 00335 * manager NULL will be returned. 00336 * 00337 * If you remove the resource from the database all resource pointers 00338 * for this resource will now point to the empty resource object. 00339 * @param name Unique name of the resource 00340 * @return either OK or error code: 00341 * - RES_NOT_FOUND 00342 * @see IResourcePtr 00343 **/ 00344 Result remove(const std::string& name); 00345 00346 //! @see remove(name) 00347 Result remove(IResourcePtr& res); 00348 00349 //! @see remove(name) 00350 Result remove(ResourceHandle& handle); 00351 00352 /** 00353 * Get the pointer to a resource by it's name. 00354 * If there is no resource with this name, so NULL pointer will be returned 00355 * 00356 * @param name Unique name of the resource 00357 * 00358 **/ 00359 IResourcePtr getByName(const std::string& name); 00360 00361 /** 00362 * Same as getByName(), but here you get the resource by handle. 00363 **/ 00364 IResourcePtr getByHandle(const ResourceHandle& handle); 00365 00366 /** 00367 * Unload all elements from the group. 00368 * @param group Unique name of the group 00369 * @return either OK or RES_GROUP_NOT_FOUND or an error code from unload(...) - method 00370 **/ 00371 Result unloadGroup(const std::string& group); 00372 00373 /** 00374 * Reload all elements in the group 00375 * @param group Unique name of the group 00376 * @return either OK or RES_GROUP_NOT_FOUND or an error code from reload(...) - method 00377 **/ 00378 Result reloadGroup(const std::string& group); 00379 00380 /** 00381 * Remove all elements in the group 00382 * @param group Unique name of the group 00383 * @return either OK or RES_GROUP_NOT_FOUND or an error code from remove(...) - method 00384 **/ 00385 Result removeGroup(const std::string& group); 00386 00387 /** 00388 * Get a list of all resource handles for a specified group. The list can be 00389 * iterated to get the resource handles which are in this group. Use the handles to 00390 * get a resource according to it. 00391 * 00392 * @param name Unique name of the group 00393 * @return List of resource handles containing in this group 00394 **/ 00395 const std::list<ResourceHandle>& getGroupHandles(const std::string& name); 00396 00397 00398 //! Typedef for the resource map returned by the getResourceMap() method 00399 typedef std::map< std::string, std::list<ResourceHandle> > ResourceGroupMap; 00400 00401 /** 00402 * Return the map containing group names and according resource list. 00403 **/ 00404 const ResourceGroupMap& getResourceMap() { return mResourceGroup; } 00405 00406 private: 00407 00408 /** 00409 * Release all resources. This will unload and then remove all 00410 * resources from the manager. Use this to clean used memory. 00411 **/ 00412 void removeAllRes(); 00413 00414 /** 00415 * Remove all loaders from the manager 00416 **/ 00417 void removeAllLoaders(); 00418 00419 00420 //! Only engine can create the instance 00421 friend class Engine; 00422 00423 /** 00424 * The constructor is protected, so only friends (engine's core) 00425 * are able to create the instance 00426 **/ 00427 ResourceManager(); 00428 00429 00430 //------------------------------------------ 00431 // Variables 00432 //------------------------------------------ 00433 //size_t mMemBudget; 00434 //size_t mMemUsage; 00435 00436 ResourceHandle mLastHandle; 00437 00438 typedef std::map< std::string, ResourceLoader> loader_map; 00439 typedef std::map<ResourceHandle, SharedPtr<ResourceHolder> > res_hdl_map; 00440 typedef std::map< std::string, ResourceHandle> res_str_map; 00441 typedef std::map< std::string, SharedPtr<IResource> > res_empty_map; 00442 00443 loader_map mLoader; 00444 00445 00446 res_hdl_map mResource; 00447 res_str_map mResourceName; 00448 ResourceGroupMap mResourceGroup; 00449 res_empty_map mEmptyResource; 00450 00451 // this list shouldn't be filled. it will be returned if no group were found in getGroupHandles() method 00452 std::list<ResourceHandle> mEmptyResourceGroupHandleList; 00453 00454 //------------------------------------------ 00455 // Methods 00456 //------------------------------------------ 00457 00458 #if 0 00459 /** 00460 * Check if we have now memory available. 00461 * If we need to remove some resources from the memory to get free place 00462 * so do it. 00463 **/ 00464 virtual Result checkMemoryUsage(); 00465 /** 00466 * This will remove the resource from the database. 00467 * After you removed the resource and would try to access to it through the 00468 * manager NULL will be returned. 00469 * 00470 * However if you remove the resource it is not just deleted from the memory 00471 * because of shared pointer system. So the parts of application, wich gets 00472 * pointers to it, are still able to access to it. 00473 * 00474 * @param resPtr Remove the resource whithin this pointer 00475 * @return either OK or error code: 00476 * - RES_NOT_FOUND 00477 **/ 00478 Result removeImpl(IResourcePtr& resPtr); 00479 #endif 00480 00481 00482 /** 00483 * Get the appropriate holder for the given resource name. 00484 * This is a internal function, so it will return a pointer to a smart pointer 00485 * pointing to the holder. This is done because of performance reasons. 00486 * If nothing found NULL will be returned. 00487 **/ 00488 SharedPtr<ResourceHolder>* getHolderByName(const std::string& name); 00489 00490 /** 00491 * Same as getHolderByName(...) but now get the holder through a handle 00492 **/ 00493 SharedPtr<ResourceHolder>* getHolderByHandle(const ResourceHandle& handle); 00494 00495 #if 0 00496 /** 00497 * This function will check if there is already an empty resource for the given resource 00498 * type and will create such one if it is not exists 00499 * @param resourceType Unique name of a resource type 00500 * @param empty Reference to smart pointer containing an empty resource after 00501 * @param loader Appropritate loader for that resource type 00502 **/ 00503 virtual Result checkEmptyResource(const std::string resourceType, 00504 SharedPtr<IResource>& empty, 00505 ResourceLoader loader); 00506 #endif 00507 00508 private: 00509 00510 //! Resource loader have access to certain functions 00511 friend class IResourceLoader; 00512 00513 //! Resource object hast got access to certain objects 00514 friend class IResource; 00515 00516 /** 00517 * Get an empty resource of given type. 00518 * 00519 * @param type Type of the resource for which one the empty object should be returned 00520 **/ 00521 SharedPtr<IResource> getEmpty(const std::string& type); 00522 00523 /** 00524 * Setup a new empty resource for the given resource type. 00525 **/ 00526 void setEmpty(const std::string& type, SharedPtr<IResource> empty); 00527 00528 /** 00529 * Check if a resource with the given name already registered in 00530 * the database. 00531 **/ 00532 bool isResourceRegistered(const std::string& name); 00533 00534 /** 00535 * Notify the manager about newly created resource. 00536 * Manager will create a holder for this 00537 * and store the things in the database 00538 **/ 00539 void notifyCreated(IResource*); 00540 00541 /** 00542 * This method is called by the loaders to notify the database, 00543 * that the according resource was loaded 00544 **/ 00545 void notifyLoaded(IResource*); 00546 00547 /** 00548 * Notify the database, that a certain resource was unloaded. 00549 **/ 00550 void notifyUnloaded(IResource*); 00551 00552 /** 00553 * Notify the database that a certain resource was removed from the 00554 * memory. After this method returns the resource will be deleted. 00555 **/ 00556 void notifyRemove(IResource*); 00557 00558 /** 00559 * Get a new handle for the resource object. Handles are unique. 00560 **/ 00561 NR_FORCEINLINE ResourceHandle getNewHandle() { return ++mLastHandle; } 00562 00563 //! Load any resource from the script 00564 ScriptFunctionDef(scriptLoadResource); 00565 00566 //! Unload any resource from scripts 00567 ScriptFunctionDef(scriptUnloadResource); 00568 00569 }; 00570 00571 }; 00572 00573 #endif