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 | |
9 | using namespace std::placeholders; |
10 | |
11 | namespace 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 | } |