resource_manager.cpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. /*
  2. * Copyright (c) 2012-2024 Daniele Bartolini et al.
  3. * SPDX-License-Identifier: MIT
  4. */
  5. #include "core/containers/array.inl"
  6. #include "core/containers/hash_map.inl"
  7. #include "core/memory/temp_allocator.inl"
  8. #include "core/strings/dynamic_string.inl"
  9. #include "core/strings/string_id.inl"
  10. #include "resource/resource_id.inl"
  11. #include "resource/resource_loader.h"
  12. #include "resource/resource_manager.h"
  13. namespace crown
  14. {
  15. bool operator<(const ResourceManager::ResourcePair &a, const ResourceManager::ResourcePair &b)
  16. {
  17. return a.type < b.type
  18. || (a.type == b.type && a.name < b.name)
  19. ;
  20. }
  21. bool operator==(const ResourceManager::ResourcePair &a, const ResourceManager::ResourcePair &b)
  22. {
  23. return a.type == b.type
  24. && a.name == b.name
  25. ;
  26. }
  27. bool operator==(const ResourceManager::ResourceData &a, const ResourceManager::ResourceData &b)
  28. {
  29. return a.references == b.references
  30. && a.online_sequence_num == b.online_sequence_num
  31. && a.allocator == b.allocator
  32. && a.data == b.data
  33. ;
  34. }
  35. bool operator!=(const ResourceManager::ResourceData &a, const ResourceManager::ResourceData &b)
  36. {
  37. return !(a == b);
  38. }
  39. const ResourceManager::ResourceData ResourceManager::ResourceData::NOT_FOUND = { UINT32_MAX, 0u, NULL, NULL };
  40. bool operator==(const ResourceManager::ResourceTypeData &a, const ResourceManager::ResourceTypeData &b)
  41. {
  42. return a.version == b.version
  43. && a.load == b.load
  44. && a.online == b.online
  45. && a.offline == b.offline
  46. && a.unload == b.unload
  47. ;
  48. }
  49. bool operator!=(const ResourceManager::ResourceTypeData &a, const ResourceManager::ResourceTypeData &b)
  50. {
  51. return !(a == b);
  52. }
  53. const ResourceManager::ResourceTypeData ResourceManager::ResourceTypeData::NOT_FOUND = { UINT32_MAX, NULL, NULL, NULL, NULL };
  54. template<>
  55. struct hash<ResourceManager::ResourcePair>
  56. {
  57. u32 operator()(const ResourceManager::ResourcePair &val) const
  58. {
  59. return u32(resource_id(val.type, val.name)._id);
  60. }
  61. };
  62. namespace resource_manager_internal
  63. {
  64. void add_resource(ResourceManager &rm, StringId64 type, StringId64 name, Allocator *allocator, void *data)
  65. {
  66. ResourceManager::ResourceData rd;
  67. rd.references = 1;
  68. rd.online_sequence_num = 0;
  69. rd.allocator = allocator;
  70. rd.data = data;
  71. ResourceManager::ResourcePair id = { type, name };
  72. hash_map::set(rm._resources, id, rd);
  73. rm.on_online(type, name);
  74. }
  75. } // namespace resource_manager_internal
  76. ResourceManager::ResourceManager(ResourceLoader &rl)
  77. : _resource_heap(default_allocator(), "resource")
  78. , _loader(&rl)
  79. , _types(default_allocator())
  80. , _resources(default_allocator())
  81. , _autoload(false)
  82. {
  83. }
  84. ResourceManager::~ResourceManager()
  85. {
  86. auto cur = hash_map::begin(_resources);
  87. auto end = hash_map::end(_resources);
  88. for (; cur != end; ++cur) {
  89. HASH_MAP_SKIP_HOLE(_resources, cur);
  90. const StringId64 type = cur->first.type;
  91. const StringId64 name = cur->first.name;
  92. on_offline(type, name);
  93. on_unload(type, cur->second.allocator, cur->second.data);
  94. }
  95. }
  96. bool ResourceManager::try_load(StringId64 package_name, StringId64 type, StringId64 name, u32 online_order)
  97. {
  98. ResourcePair id = { type, name };
  99. ResourceData &rd = hash_map::get(_resources, id, ResourceData::NOT_FOUND);
  100. ResourceRequest rr;
  101. rr.resource_manager = this;
  102. rr.package_name = package_name;
  103. rr.type = type;
  104. rr.name = name;
  105. rr.online_order = online_order;
  106. rr.data = NULL;
  107. if (rd == ResourceData::NOT_FOUND) {
  108. ResourceTypeData rtd = hash_map::get(_types, type, ResourceTypeData::NOT_FOUND);
  109. CE_ENSURE(rtd != ResourceTypeData::NOT_FOUND);
  110. rr.allocator = &_resource_heap;
  111. rr.version = rtd.version;
  112. rr.load_function = rtd.load;
  113. return _loader->add_request(rr);
  114. }
  115. rd.references++;
  116. // Push a spurious loaded resource event. This avoids blocking forever
  117. // in complete_requests() by keeping the online_sequence_num updated.
  118. rr.allocator = NULL;
  119. rr.version = 0;
  120. rr.load_function = NULL;
  121. _loader->_loaded.push(rr);
  122. return true;
  123. }
  124. void ResourceManager::unload(StringId64 type, StringId64 name)
  125. {
  126. ResourcePair id = { type, name };
  127. ResourceData &rd = hash_map::get(_resources, id, ResourceData::NOT_FOUND);
  128. if (--rd.references == 0) {
  129. on_offline(type, name);
  130. on_unload(type, rd.allocator, rd.data);
  131. hash_map::remove(_resources, id);
  132. }
  133. }
  134. void *ResourceManager::reload(StringId64 type, StringId64 name)
  135. {
  136. const ResourcePair id = { type, name };
  137. const ResourceData &rd = hash_map::get(_resources, id, ResourceData::NOT_FOUND);
  138. if (rd == ResourceData::NOT_FOUND)
  139. return NULL;
  140. const u32 old_refs = rd.references;
  141. unload(type, name);
  142. while (!try_load(PACKAGE_RESOURCE_NONE, type, name, 0)) {
  143. complete_requests();
  144. }
  145. ResourceData new_rd;
  146. while ((new_rd = hash_map::get(_resources, id, ResourceData::NOT_FOUND)) == ResourceData::NOT_FOUND)
  147. complete_requests();
  148. new_rd.references = old_refs;
  149. return new_rd.data;
  150. }
  151. bool ResourceManager::can_get(StringId64 type, StringId64 name)
  152. {
  153. const ResourcePair id = { type, name };
  154. return _autoload ? true : hash_map::has(_resources, id);
  155. }
  156. const void *ResourceManager::get(StringId64 type, StringId64 name)
  157. {
  158. CE_ASSERT(can_get(type, name), "Resource not loaded: " RESOURCE_ID_FMT, resource_id(type, name)._id);
  159. const ResourcePair id = { type, name };
  160. if (_autoload && !hash_map::has(_resources, id)) {
  161. while (!try_load(PACKAGE_RESOURCE_NONE, type, name, 0)) {
  162. complete_requests();
  163. }
  164. while (!hash_map::has(_resources, id)) {
  165. complete_requests();
  166. }
  167. }
  168. return hash_map::get(_resources, id, ResourceData::NOT_FOUND).data;
  169. }
  170. void ResourceManager::enable_autoload(bool enable)
  171. {
  172. _autoload = enable;
  173. }
  174. void ResourceManager::complete_requests()
  175. {
  176. ResourceRequest rr;
  177. while (_loader->_loaded.pop(rr)) {
  178. if (rr.type == RESOURCE_TYPE_PACKAGE || rr.type == RESOURCE_TYPE_CONFIG || _autoload) {
  179. // Always add packages and configs to the resource map because they never have
  180. // requirements and are never required by any resource, hence no online() order
  181. // constraints apply.
  182. resource_manager_internal::add_resource(*this
  183. , rr.type
  184. , rr.name
  185. , rr.allocator
  186. , rr.data
  187. );
  188. } else {
  189. ResourcePair rp { RESOURCE_TYPE_PACKAGE, rr.package_name };
  190. ResourceData &pkg_data = hash_map::get(_resources, rp, ResourceData::NOT_FOUND);
  191. CE_ENSURE(pkg_data != ResourceData::NOT_FOUND);
  192. if (rr.online_order != pkg_data.online_sequence_num) {
  193. // Cannot process this resource yet; we need to wait for all its requirements to be
  194. // put online() first. Put the request back into the loaded queue to try again
  195. // later.
  196. _loader->_loaded.push(rr);
  197. } else {
  198. ++pkg_data.online_sequence_num;
  199. if (rr.data != NULL && rr.allocator != NULL) {
  200. // If this is a non-spurious request, add it to the resource map.
  201. resource_manager_internal::add_resource(*this
  202. , rr.type
  203. , rr.name
  204. , rr.allocator
  205. , rr.data
  206. );
  207. }
  208. }
  209. }
  210. }
  211. }
  212. void ResourceManager::register_type(StringId64 type, u32 version, LoadFunction load, UnloadFunction unload, OnlineFunction online, OfflineFunction offline)
  213. {
  214. ResourceTypeData rtd;
  215. rtd.version = version;
  216. rtd.load = load;
  217. rtd.online = online;
  218. rtd.offline = offline;
  219. rtd.unload = unload;
  220. hash_map::set(_types, type, rtd);
  221. }
  222. void ResourceManager::on_online(StringId64 type, StringId64 name)
  223. {
  224. OnlineFunction func = hash_map::get(_types, type, ResourceTypeData::NOT_FOUND).online;
  225. if (func)
  226. func(name, *this);
  227. }
  228. void ResourceManager::on_offline(StringId64 type, StringId64 name)
  229. {
  230. OfflineFunction func = hash_map::get(_types, type, ResourceTypeData::NOT_FOUND).offline;
  231. if (func)
  232. func(name, *this);
  233. }
  234. void ResourceManager::on_unload(StringId64 type, Allocator *allocator, void *data)
  235. {
  236. if (allocator == NULL)
  237. return;
  238. UnloadFunction func = hash_map::get(_types, type, ResourceTypeData::NOT_FOUND).unload;
  239. if (func)
  240. func(*allocator, data);
  241. else
  242. allocator->deallocate(data);
  243. }
  244. } // namespace crown