resource_manager.cpp 8.7 KB

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