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