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 "Managers/BsResourceListenerManager.h"
4#include "Resources/BsResources.h"
5#include "Resources/BsIResourceListener.h"
6#include "CoreThread/BsCoreThread.h"
7#include "BsCoreApplication.h"
8
9using namespace std::placeholders;
10
11namespace bs
12{
13#if BS_DEBUG_MODE
14 void throwIfNotSimThread()
15 {
16 if(BS_THREAD_CURRENT_ID != CoreApplication::instance().getSimThreadId())
17 BS_EXCEPT(InternalErrorException, "This method can only be accessed from the simulation thread.");
18 }
19
20#define THROW_IF_NOT_SIM_THREAD throwIfNotSimThread();
21#else
22#define THROW_IF_NOT_SIM_THREAD
23#endif
24
25 ResourceListenerManager::ResourceListenerManager()
26 {
27 mResourceLoadedConn = gResources().onResourceLoaded.connect(std::bind(&ResourceListenerManager::onResourceLoaded, this, _1));
28 mResourceModifiedConn = gResources().onResourceModified.connect(std::bind(&ResourceListenerManager::onResourceModified, this, _1));
29 }
30
31 ResourceListenerManager::~ResourceListenerManager()
32 {
33 assert(mResourceToListenerMap.empty() && "Not all resource listeners had their resources unregistered properly.");
34
35 mResourceLoadedConn.disconnect();
36 mResourceModifiedConn.disconnect();
37 }
38
39 void ResourceListenerManager::registerListener(IResourceListener* listener)
40 {
41#if BS_DEBUG_MODE
42 RecursiveLock lock(mMutex);
43 mActiveListeners.insert(listener);
44#endif
45 }
46
47 void ResourceListenerManager::unregisterListener(IResourceListener* listener)
48 {
49#if BS_DEBUG_MODE
50 {
51 RecursiveLock lock(mMutex);
52 mActiveListeners.erase(listener);
53 }
54#endif
55
56 {
57 RecursiveLock lock(mMutex);
58 mDirtyListeners.erase(listener);
59 }
60
61 clearDependencies(listener);
62 }
63
64 void ResourceListenerManager::markListenerDirty(IResourceListener* listener)
65 {
66 RecursiveLock lock(mMutex);
67 mDirtyListeners.insert(listener);
68 }
69
70 void ResourceListenerManager::update()
71 {
72 THROW_IF_NOT_SIM_THREAD
73 updateListeners();
74
75 {
76 RecursiveLock lock(mMutex);
77
78 for (auto& entry : mLoadedResources)
79 sendResourceLoaded(entry.second);
80
81 for (auto& entry : mModifiedResources)
82 sendResourceModified(entry.second);
83
84 mLoadedResources.clear();
85 mModifiedResources.clear();
86 }
87 }
88
89 void ResourceListenerManager::updateListeners()
90 {
91 {
92 RecursiveLock lock(mMutex);
93
94 for (auto& listener : mDirtyListeners)
95 mTempListenerBuffer.push_back(listener);
96
97 mDirtyListeners.clear();
98 }
99
100 for (auto& listener : mTempListenerBuffer)
101 {
102 clearDependencies(listener);
103 addDependencies(listener);
104 }
105
106 mTempListenerBuffer.clear();
107
108 }
109
110 void ResourceListenerManager::notifyListeners(const UUID& resourceUUID)
111 {
112 THROW_IF_NOT_SIM_THREAD
113 updateListeners();
114
115 HResource loadedResource;
116 {
117 RecursiveLock lock(mMutex);
118
119 const auto iterFind = mLoadedResources.find(resourceUUID);
120 if (iterFind != mLoadedResources.end())
121 {
122 loadedResource = std::move(iterFind->second);
123 mLoadedResources.erase(iterFind);
124 }
125 }
126
127 if(loadedResource)
128 sendResourceLoaded(loadedResource);
129
130 HResource modifiedResource;
131 {
132 RecursiveLock lock(mMutex);
133
134 const auto iterFind = mModifiedResources.find(resourceUUID);
135 if (iterFind != mModifiedResources.end())
136 {
137 modifiedResource = std::move(iterFind->second);
138 mModifiedResources.erase(iterFind);
139 }
140 }
141
142 if(modifiedResource)
143 sendResourceModified(modifiedResource);
144 }
145
146 void ResourceListenerManager::onResourceLoaded(const HResource& resource)
147 {
148 RecursiveLock lock(mMutex);
149
150 mLoadedResources[resource.getUUID()] = resource;
151 }
152
153 void ResourceListenerManager::onResourceModified(const HResource& resource)
154 {
155 RecursiveLock lock(mMutex);
156
157 mModifiedResources[resource.getUUID()] = resource;
158 }
159
160 void ResourceListenerManager::sendResourceLoaded(const HResource& resource)
161 {
162 UINT64 handleId = (UINT64)resource.getHandleData().get();
163
164 auto iterFind = mResourceToListenerMap.find(handleId);
165 if (iterFind == mResourceToListenerMap.end())
166 return;
167
168 const Vector<IResourceListener*> relevantListeners = iterFind->second;
169 for (auto& listener : relevantListeners)
170 {
171#if BS_DEBUG_MODE
172 assert(mActiveListeners.find(listener) != mActiveListeners.end() && "Attempting to notify a destroyed IResourceListener");
173#endif
174
175 listener->notifyResourceLoaded(resource);
176 }
177 }
178
179 void ResourceListenerManager::sendResourceModified(const HResource& resource)
180 {
181 UINT64 handleId = (UINT64)resource.getHandleData().get();
182
183 auto iterFind = mResourceToListenerMap.find(handleId);
184 if (iterFind == mResourceToListenerMap.end())
185 return;
186
187 const Vector<IResourceListener*> relevantListeners = iterFind->second;
188 for (auto& listener : relevantListeners)
189 {
190#if BS_DEBUG_MODE
191 assert(mActiveListeners.find(listener) != mActiveListeners.end() && "Attempting to notify a destroyed IResourceListener");
192#endif
193
194 listener->notifyResourceChanged(resource);
195 }
196 }
197
198 void ResourceListenerManager::clearDependencies(IResourceListener* listener)
199 {
200 auto iterFind = mListenerToResourceMap.find(listener);
201 if (iterFind == mListenerToResourceMap.end())
202 return;
203
204 const Vector<UINT64>& dependantResources = iterFind->second;
205 for (auto& resourceHandleId : dependantResources)
206 {
207 auto iterFind2 = mResourceToListenerMap.find(resourceHandleId);
208 if (iterFind2 != mResourceToListenerMap.end())
209 {
210 Vector<IResourceListener*>& listeners = iterFind2->second;
211 auto iterFind3 = std::find(listeners.begin(), listeners.end(), listener);
212
213 if (iterFind3 != listeners.end())
214 listeners.erase(iterFind3);
215
216 if (listeners.size() == 0)
217 mResourceToListenerMap.erase(iterFind2);
218 }
219 }
220
221 mListenerToResourceMap.erase(iterFind);
222 }
223
224 void ResourceListenerManager::addDependencies(IResourceListener* listener)
225 {
226 listener->getListenerResources(mTempResourceBuffer);
227
228 if (mTempResourceBuffer.size() > 0)
229 {
230 Vector<UINT64> resourceHandleIds(mTempResourceBuffer.size());
231 UINT32 idx = 0;
232 for (auto& resource : mTempResourceBuffer)
233 {
234 UINT64 handleId = (UINT64)resource.getHandleData().get();
235 resourceHandleIds[idx] = handleId;
236 mResourceToListenerMap[handleId].push_back(listener);
237
238 idx++;
239 }
240
241 mListenerToResourceMap[listener] = resourceHandleIds;
242 }
243
244 mTempResourceBuffer.clear();
245 }
246}