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 | #include "CoreThread/BsCoreObjectManager.h" |
4 | #include "CoreThread/BsCoreObject.h" |
5 | #include "CoreThread/BsCoreObjectCore.h" |
6 | #include "Error/BsException.h" |
7 | #include "Math/BsMath.h" |
8 | #include "CoreThread/BsCoreThread.h" |
9 | |
10 | namespace bs |
11 | { |
12 | CoreObjectManager::CoreObjectManager() |
13 | :mNextAvailableID(1) |
14 | { |
15 | |
16 | } |
17 | |
18 | CoreObjectManager::~CoreObjectManager() |
19 | { |
20 | #if BS_DEBUG_MODE |
21 | Lock lock(mObjectsMutex); |
22 | |
23 | if(mObjects.size() > 0) |
24 | { |
25 | // All objects MUST be destroyed at this point, otherwise there might be memory corruption. |
26 | // (Reason: This is called on application shutdown and at that point we also unload any dynamic libraries, |
27 | // which will invalidate any pointers to objects created from those libraries. Therefore we require of the user to |
28 | // clean up all objects manually before shutting down the application). |
29 | BS_EXCEPT(InternalErrorException, "Core object manager shut down, but not all objects were released. Application must release ALL " \ |
30 | "engine objects before shutdown." ); |
31 | } |
32 | #endif |
33 | } |
34 | |
35 | UINT64 CoreObjectManager::generateId() |
36 | { |
37 | Lock lock(mObjectsMutex); |
38 | |
39 | return mNextAvailableID++; |
40 | } |
41 | |
42 | void CoreObjectManager::registerObject(CoreObject* object) |
43 | { |
44 | Lock lock(mObjectsMutex); |
45 | |
46 | UINT64 objId = object->getInternalID(); |
47 | mObjects[objId] = object; |
48 | mDirtyObjects[objId] = { object, -1 }; |
49 | } |
50 | |
51 | void CoreObjectManager::unregisterObject(CoreObject* object) |
52 | { |
53 | assert(object != nullptr && !object->isDestroyed()); |
54 | |
55 | UINT64 internalId = object->getInternalID(); |
56 | |
57 | // If dirty, we generate sync data before it is destroyed |
58 | { |
59 | Lock lock(mObjectsMutex); |
60 | bool isDirty = object->isCoreDirty() || (mDirtyObjects.find(internalId) != mDirtyObjects.end()); |
61 | |
62 | if (isDirty) |
63 | { |
64 | SPtr<ct::CoreObject> coreObject = object->getCore(); |
65 | if (coreObject != nullptr) |
66 | { |
67 | CoreSyncData objSyncData = object->syncToCore(gCoreThread().getFrameAlloc()); |
68 | |
69 | mDestroyedSyncData.push_back(CoreStoredSyncObjData(coreObject, internalId, objSyncData)); |
70 | |
71 | DirtyObjectData& dirtyObjData = mDirtyObjects[internalId]; |
72 | dirtyObjData.syncDataId = (INT32)mDestroyedSyncData.size() - 1; |
73 | dirtyObjData.object = nullptr; |
74 | } |
75 | else |
76 | { |
77 | DirtyObjectData& dirtyObjData = mDirtyObjects[internalId]; |
78 | dirtyObjData.syncDataId = -1; |
79 | dirtyObjData.object = nullptr; |
80 | } |
81 | } |
82 | |
83 | mObjects.erase(internalId); |
84 | } |
85 | |
86 | updateDependencies(object, nullptr); |
87 | |
88 | // Clear dependencies from dependants |
89 | { |
90 | Lock lock(mObjectsMutex); |
91 | |
92 | auto iterFind = mDependants.find(internalId); |
93 | if (iterFind != mDependants.end()) |
94 | { |
95 | Vector<CoreObject*>& dependants = iterFind->second; |
96 | for (auto& entry : dependants) |
97 | { |
98 | auto iterFind2 = mDependencies.find(entry->getInternalID()); |
99 | if (iterFind2 != mDependencies.end()) |
100 | { |
101 | Vector<CoreObject*>& dependencies = iterFind2->second; |
102 | auto iterFind3 = std::find(dependencies.begin(), dependencies.end(), object); |
103 | |
104 | if (iterFind3 != dependencies.end()) |
105 | dependencies.erase(iterFind3); |
106 | |
107 | if (dependencies.size() == 0) |
108 | mDependencies.erase(iterFind2); |
109 | } |
110 | } |
111 | |
112 | mDependants.erase(iterFind); |
113 | } |
114 | |
115 | mDependencies.erase(internalId); |
116 | } |
117 | } |
118 | |
119 | void CoreObjectManager::notifyCoreDirty(CoreObject* object) |
120 | { |
121 | UINT64 id = object->getInternalID(); |
122 | |
123 | Lock lock(mObjectsMutex); |
124 | |
125 | mDirtyObjects[id] = { object, -1 }; |
126 | } |
127 | |
128 | void CoreObjectManager::notifyDependenciesDirty(CoreObject* object) |
129 | { |
130 | Vector<CoreObject*> dependencies; |
131 | object->getCoreDependencies(dependencies); |
132 | |
133 | updateDependencies(object, &dependencies); |
134 | } |
135 | |
136 | void CoreObjectManager::updateDependencies(CoreObject* object, Vector<CoreObject*>* dependencies) |
137 | { |
138 | UINT64 id = object->getInternalID(); |
139 | |
140 | bs_frame_mark(); |
141 | { |
142 | FrameVector<CoreObject*> toRemove; |
143 | FrameVector<CoreObject*> toAdd; |
144 | |
145 | Lock lock(mObjectsMutex); |
146 | |
147 | // Add dependencies and clear old dependencies from dependants |
148 | { |
149 | if (dependencies != nullptr) |
150 | std::sort(dependencies->begin(), dependencies->end()); |
151 | |
152 | auto iterFind = mDependencies.find(id); |
153 | if (iterFind != mDependencies.end()) |
154 | { |
155 | const Vector<CoreObject*>& oldDependencies = iterFind->second; |
156 | |
157 | if (dependencies != nullptr) |
158 | { |
159 | std::set_difference(oldDependencies.begin(), oldDependencies.end(), |
160 | dependencies->begin(), dependencies->end(), std::inserter(toRemove, toRemove.begin())); |
161 | |
162 | std::set_difference(dependencies->begin(), dependencies->end(), |
163 | oldDependencies.begin(), oldDependencies.end(), std::inserter(toAdd, toAdd.begin())); |
164 | } |
165 | else |
166 | { |
167 | for (auto& dependency : oldDependencies) |
168 | toRemove.push_back(dependency); |
169 | } |
170 | |
171 | for (auto& dependency : toRemove) |
172 | { |
173 | UINT64 dependencyId = dependency->getInternalID(); |
174 | auto iterFind2 = mDependants.find(dependencyId); |
175 | |
176 | if (iterFind2 != mDependants.end()) |
177 | { |
178 | Vector<CoreObject*>& dependants = iterFind2->second; |
179 | auto findIter3 = std::find(dependants.begin(), dependants.end(), object); |
180 | dependants.erase(findIter3); |
181 | |
182 | if (dependants.size() == 0) |
183 | mDependants.erase(iterFind2); |
184 | } |
185 | } |
186 | |
187 | if (dependencies != nullptr && dependencies->size() > 0) |
188 | mDependencies[id] = *dependencies; |
189 | else |
190 | mDependencies.erase(id); |
191 | } |
192 | else |
193 | { |
194 | if (dependencies != nullptr && dependencies->size() > 0) |
195 | { |
196 | for (auto& dependency : *dependencies) |
197 | toAdd.push_back(dependency); |
198 | |
199 | mDependencies[id] = *dependencies; |
200 | } |
201 | } |
202 | } |
203 | |
204 | // Register dependants |
205 | { |
206 | for (auto& dependency : toAdd) |
207 | { |
208 | UINT64 dependencyId = dependency->getInternalID(); |
209 | Vector<CoreObject*>& dependants = mDependants[dependencyId]; |
210 | dependants.push_back(object); |
211 | } |
212 | } |
213 | } |
214 | bs_frame_clear(); |
215 | } |
216 | |
217 | void CoreObjectManager::syncToCore() |
218 | { |
219 | syncDownload(gCoreThread().getFrameAlloc()); |
220 | gCoreThread().queueCommand(std::bind(&CoreObjectManager::syncUpload, this)); |
221 | } |
222 | |
223 | void CoreObjectManager::syncToCore(CoreObject* object) |
224 | { |
225 | struct IndividualCoreSyncData |
226 | { |
227 | SPtr<ct::CoreObject> destination; |
228 | CoreSyncData syncData; |
229 | FrameAlloc* allocator; |
230 | }; |
231 | |
232 | Lock lock(mObjectsMutex); |
233 | |
234 | FrameAlloc* allocator = gCoreThread().getFrameAlloc(); |
235 | Vector<IndividualCoreSyncData> syncData; |
236 | |
237 | std::function<void(CoreObject*)> syncObject = [&](CoreObject* curObj) |
238 | { |
239 | if (!curObj->isCoreDirty()) |
240 | return; // We already processed it as some other object's dependency |
241 | |
242 | // Sync dependencies before dependants |
243 | // Note: I don't check for recursion. Possible infinite loop if two objects |
244 | // are dependent on one another. |
245 | |
246 | UINT64 id = curObj->getInternalID(); |
247 | auto iterFind = mDependencies.find(id); |
248 | |
249 | if (iterFind != mDependencies.end()) |
250 | { |
251 | const Vector<CoreObject*>& dependencies = iterFind->second; |
252 | for (auto& dependency : dependencies) |
253 | syncObject(dependency); |
254 | } |
255 | |
256 | SPtr<ct::CoreObject> objectCore = curObj->getCore(); |
257 | if (objectCore == nullptr) |
258 | { |
259 | curObj->markCoreClean(); |
260 | mDirtyObjects.erase(id); |
261 | return; |
262 | } |
263 | |
264 | syncData.push_back(IndividualCoreSyncData()); |
265 | IndividualCoreSyncData& data = syncData.back(); |
266 | data.allocator = allocator; |
267 | data.destination = objectCore; |
268 | data.syncData = curObj->syncToCore(allocator); |
269 | |
270 | curObj->markCoreClean(); |
271 | mDirtyObjects.erase(id); |
272 | }; |
273 | |
274 | syncObject(object); |
275 | |
276 | std::function<void(const Vector<IndividualCoreSyncData>&)> callback = |
277 | [](const Vector<IndividualCoreSyncData>& data) |
278 | { |
279 | // Traverse in reverse to sync dependencies before dependants |
280 | for (auto riter = data.rbegin(); riter != data.rend(); ++riter) |
281 | { |
282 | const IndividualCoreSyncData& entry = *riter; |
283 | entry.destination->syncToCore(entry.syncData); |
284 | |
285 | UINT8* dataPtr = entry.syncData.getBuffer(); |
286 | |
287 | if (dataPtr != nullptr) |
288 | entry.allocator->free(dataPtr); |
289 | } |
290 | }; |
291 | |
292 | if (syncData.size() > 0) |
293 | gCoreThread().queueCommand(std::bind(callback, syncData)); |
294 | } |
295 | |
296 | void CoreObjectManager::syncDownload(FrameAlloc* allocator) |
297 | { |
298 | Lock lock(mObjectsMutex); |
299 | |
300 | mCoreSyncData.push_back(CoreStoredSyncData()); |
301 | CoreStoredSyncData& syncData = mCoreSyncData.back(); |
302 | |
303 | syncData.alloc = allocator; |
304 | |
305 | // Add all objects dependant on the dirty objects |
306 | bs_frame_mark(); |
307 | { |
308 | FrameSet<CoreObject*> dirtyDependants; |
309 | for (auto& objectData : mDirtyObjects) |
310 | { |
311 | auto iterFind = mDependants.find(objectData.first); |
312 | if (iterFind != mDependants.end()) |
313 | { |
314 | const Vector<CoreObject*>& dependants = iterFind->second; |
315 | for (auto& dependant : dependants) |
316 | { |
317 | const bool wasDirty = dependant->isCoreDirty(); |
318 | |
319 | // Let the dependant objects know their dependency changed |
320 | CoreObject* dependency = objectData.second.object; |
321 | dependant->onDependencyDirty(dependency, dependency->getCoreDirtyFlags()); |
322 | |
323 | if (!wasDirty && dependant->isCoreDirty()) |
324 | dirtyDependants.insert(dependant); |
325 | } |
326 | } |
327 | } |
328 | |
329 | for (auto& dirtyDependant : dirtyDependants) |
330 | { |
331 | UINT64 id = dirtyDependant->getInternalID(); |
332 | |
333 | mDirtyObjects[id] = { dirtyDependant, -1 }; |
334 | } |
335 | } |
336 | |
337 | bs_frame_clear(); |
338 | |
339 | // Order in which objects are recursed in matters, ones with lower ID will have been created before |
340 | // ones with higher ones and should be updated first. |
341 | for (auto& objectData : mDirtyObjects) |
342 | { |
343 | std::function<void(CoreObject*)> syncObject = [&](CoreObject* curObj) |
344 | { |
345 | if (!curObj->isCoreDirty()) |
346 | return; // We already processed it as some other object's dependency |
347 | |
348 | // Sync dependencies before dependants |
349 | // Note: I don't check for recursion. Possible infinite loop if two objects |
350 | // are dependent on one another. |
351 | |
352 | UINT64 id = curObj->getInternalID(); |
353 | auto iterFind = mDependencies.find(id); |
354 | |
355 | if (iterFind != mDependencies.end()) |
356 | { |
357 | const Vector<CoreObject*>& dependencies = iterFind->second; |
358 | for (auto& dependency : dependencies) |
359 | syncObject(dependency); |
360 | } |
361 | |
362 | SPtr<ct::CoreObject> objectCore = curObj->getCore(); |
363 | if (objectCore == nullptr) |
364 | { |
365 | curObj->markCoreClean(); |
366 | return; |
367 | } |
368 | |
369 | CoreSyncData objSyncData = curObj->syncToCore(allocator); |
370 | curObj->markCoreClean(); |
371 | |
372 | syncData.entries.push_back(CoreStoredSyncObjData(objectCore, |
373 | curObj->getInternalID(), objSyncData)); |
374 | }; |
375 | |
376 | CoreObject* object = objectData.second.object; |
377 | if (object != nullptr) |
378 | syncObject(object); |
379 | else |
380 | { |
381 | // Object was destroyed but we still need to sync its modifications before it was destroyed |
382 | if (objectData.second.syncDataId != -1) |
383 | { |
384 | const CoreStoredSyncObjData& objData = mDestroyedSyncData[objectData.second.syncDataId]; |
385 | |
386 | syncData.entries.push_back(objData); |
387 | syncData.destroyedObjects.push_back(objData.destinationObj); |
388 | } |
389 | } |
390 | } |
391 | |
392 | mDirtyObjects.clear(); |
393 | mDestroyedSyncData.clear(); |
394 | } |
395 | |
396 | void CoreObjectManager::syncUpload() |
397 | { |
398 | Lock lock(mObjectsMutex); |
399 | |
400 | if (mCoreSyncData.size() == 0) |
401 | return; |
402 | |
403 | CoreStoredSyncData& syncData = mCoreSyncData.front(); |
404 | |
405 | for (auto& objSyncData : syncData.entries) |
406 | { |
407 | SPtr<ct::CoreObject> destinationObj = objSyncData.destinationObj; |
408 | if (destinationObj != nullptr) |
409 | destinationObj->syncToCore(objSyncData.syncData); |
410 | |
411 | UINT8* data = objSyncData.syncData.getBuffer(); |
412 | |
413 | if (data != nullptr) |
414 | syncData.alloc->free(data); |
415 | } |
416 | |
417 | syncData.destroyedObjects.clear(); |
418 | syncData.entries.clear(); |
419 | mCoreSyncData.pop_front(); |
420 | } |
421 | } |