void Resources::update(HResource& handle, const SPtr<Resource>& resource) { const String& uuid = handle.getUUID(); handle.setHandleData(resource, uuid); { Lock lock(mLoadedResourceMutex); auto iterFind = mLoadedResources.find(uuid); if (iterFind == mLoadedResources.end()) { LoadedResourceData& resData = mLoadedResources[uuid]; resData.resource = handle.getWeak(); } } onResourceModified(handle); }
HResource Resources::loadInternal(const String& UUID, const Path& filePath, bool synchronous, bool loadDependencies, bool keepInternalReference) { HResource outputResource; bool alreadyLoading = false; bool loadInProgress = false; { // Check if resource is already being loaded on a worker thread Lock inProgressLock(mInProgressResourcesMutex); auto iterFind2 = mInProgressResources.find(UUID); if (iterFind2 != mInProgressResources.end()) { LoadedResourceData& resData = iterFind2->second->resData; outputResource = resData.resource.lock(); if (keepInternalReference) { resData.numInternalRefs++; outputResource.addInternalRef(); } alreadyLoading = true; loadInProgress = true; } // Previously being loaded as async but now we want it synced, so we wait if (loadInProgress && synchronous) outputResource.blockUntilLoaded(); if (!alreadyLoading) { Lock loadedLock(mLoadedResourceMutex); auto iterFind = mLoadedResources.find(UUID); if (iterFind != mLoadedResources.end()) // Resource is already loaded { LoadedResourceData& resData = iterFind->second; outputResource = resData.resource.lock(); if (keepInternalReference) { resData.numInternalRefs++; outputResource.addInternalRef(); } alreadyLoading = true; } } } // Not loaded and not in progress, start loading of new resource // (or if already loaded or in progress, load any dependencies) if (!alreadyLoading) { // Check if the handle already exists Lock lock(mLoadedResourceMutex); auto iterFind = mHandles.find(UUID); if (iterFind != mHandles.end()) outputResource = iterFind->second.lock(); else { outputResource = HResource(UUID); mHandles[UUID] = outputResource.getWeak(); } } // We have nowhere to load from, warn and complete load if a file path was provided, // otherwise pass through as we might just want to load from memory. if (filePath.isEmpty()) { if (!alreadyLoading) { LOGWRN_VERBOSE("Cannot load resource. Resource with UUID '" + UUID + "' doesn't exist."); // Complete the load as that the depedency counter is properly reduced, in case this // is a dependency of some other resource. loadComplete(outputResource); return outputResource; } } else if (!FileSystem::isFile(filePath)) { LOGWRN_VERBOSE("Cannot load resource. Specified file: " + filePath.toString() + " doesn't exist."); // Complete the load as that the depedency counter is properly reduced, in case this // is a dependency of some other resource. loadComplete(outputResource); assert(!loadInProgress); // Resource already being loaded but we can't find its path now? return outputResource; } // Load dependency data if a file path is provided SPtr<SavedResourceData> savedResourceData; if (!filePath.isEmpty()) { FileDecoder fs(filePath); savedResourceData = std::static_pointer_cast<SavedResourceData>(fs.decode()); } // If already loading keep the old load operation active, otherwise create a new one if (!alreadyLoading) { { Lock lock(mInProgressResourcesMutex); ResourceLoadData* loadData = bs_new<ResourceLoadData>(outputResource.getWeak(), 0); mInProgressResources[UUID] = loadData; loadData->resData = outputResource.getWeak(); if (keepInternalReference) { loadData->resData.numInternalRefs++; outputResource.addInternalRef(); } loadData->remainingDependencies = 1; loadData->notifyImmediately = synchronous; // Make resource listener trigger before exit if loading synchronously // Register dependencies and count them so we know when the resource is fully loaded if (loadDependencies && savedResourceData != nullptr) { for (auto& dependency : savedResourceData->getDependencies()) { if (dependency != UUID) { mDependantLoads[dependency].push_back(loadData); loadData->remainingDependencies++; } } } } if (loadDependencies && savedResourceData != nullptr) { const Vector<String>& dependencyUUIDs = savedResourceData->getDependencies(); UINT32 numDependencies = (UINT32)dependencyUUIDs.size(); Vector<HResource> dependencies(numDependencies); for (UINT32 i = 0; i < numDependencies; i++) dependencies[i] = loadFromUUID(dependencyUUIDs[i], !synchronous, true, false); // Keep dependencies alive until the parent is done loading { Lock lock(mInProgressResourcesMutex); // At this point the resource is guaranteed to still be in-progress, so it's safe to update its dependency list mInProgressResources[UUID]->dependencies = dependencies; } } } else if (loadDependencies && savedResourceData != nullptr) // Queue dependencies in case they aren't already loaded { const Vector<String>& dependencies = savedResourceData->getDependencies(); if (!dependencies.empty()) { { Lock lock(mInProgressResourcesMutex); ResourceLoadData* loadData = nullptr; auto iterFind = mInProgressResources.find(UUID); if (iterFind == mInProgressResources.end()) // Fully loaded { loadData = bs_new<ResourceLoadData>(outputResource.getWeak(), 0); loadData->resData = outputResource.getWeak(); loadData->remainingDependencies = 0; loadData->notifyImmediately = synchronous; // Make resource listener trigger before exit if loading synchronously mInProgressResources[UUID] = loadData; } else { loadData = iterFind->second; } // Register dependencies and count them so we know when the resource is fully loaded for (auto& dependency : dependencies) { if (dependency != UUID) { bool registerDependency = true; auto iterFind2 = mDependantLoads.find(dependency); if (iterFind2 != mDependantLoads.end()) { Vector<ResourceLoadData*>& dependantData = iterFind2->second; auto iterFind3 = std::find_if(dependantData.begin(), dependantData.end(), [&](ResourceLoadData* x) { return x->resData.resource.getUUID() == outputResource.getUUID(); }); registerDependency = iterFind3 == dependantData.end(); } if (registerDependency) { mDependantLoads[dependency].push_back(loadData); loadData->remainingDependencies++; loadData->dependencies.push_back(_getResourceHandle(dependency)); } } } } for (auto& dependency : dependencies) loadFromUUID(dependency, !synchronous, true, false); } } // Actually start the file read operation if not already loaded or in progress if (!alreadyLoading && !filePath.isEmpty()) { // Synchronous or the resource doesn't support async, read the file immediately if (synchronous || !savedResourceData->allowAsyncLoading()) { loadCallback(filePath, outputResource); } else // Asynchronous, read the file on a worker thread { String fileName = filePath.getFilename(); String taskName = "Resource load: " + fileName; SPtr<Task> task = Task::create(taskName, std::bind(&Resources::loadCallback, this, filePath, outputResource)); TaskScheduler::instance().addTask(task); } } else // File already loaded or in progress { // Complete the load unless its in progress in which case we wait for its worker thread to complete it. // In case file is already loaded this will only decrement dependency count in case this resource is a dependency. if (!loadInProgress) loadComplete(outputResource); else { // In case loading finished in the meantime we cannot be sure at what point ::loadComplete was triggered, // so trigger it manually so that the dependency count is properly decremented in case this resource // is a dependency. Lock lock(mLoadedResourceMutex); auto iterFind = mLoadedResources.find(UUID); if (iterFind != mLoadedResources.end()) loadComplete(outputResource); } } return outputResource; }