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