void Resources::save(const HResource& resource) { if (resource == nullptr) return; Path path; if (getFilePathFromUUID(resource.getUUID(), path)) save(resource, path, true); }
void Resources::save(const HResource& resource, const Path& filePath, bool overwrite) { if (resource == nullptr) return; if (!resource.isLoaded(false)) { bool loadInProgress = false; { Lock lock(mInProgressResourcesMutex); auto iterFind2 = mInProgressResources.find(resource.getUUID()); if (iterFind2 != mInProgressResources.end()) loadInProgress = true; } if (loadInProgress) // If it's still loading wait until that finishes resource.blockUntilLoaded(); else return; // Nothing to save } bool fileExists = FileSystem::isFile(filePath); if(fileExists) { if(overwrite) FileSystem::remove(filePath); else BS_EXCEPT(InvalidParametersException, "Another file exists at the specified location."); } mDefaultResourceManifest->registerResource(resource.getUUID(), filePath); Vector<ResourceDependency> dependencyList = Utility::findResourceDependencies(*resource.get()); Vector<String> dependencyUUIDs(dependencyList.size()); for (UINT32 i = 0; i < (UINT32)dependencyList.size(); i++) dependencyUUIDs[i] = dependencyList[i].resource.getUUID(); SPtr<SavedResourceData> resourceData = bs_shared_ptr_new<SavedResourceData>(dependencyUUIDs, resource->allowAsyncLoading()); FileEncoder fs(filePath); fs.encode(resourceData.get()); fs.encode(resource.get()); }
void BuiltinResources::generateTextures() { StringStream ss; SPtr<PixelData> blackPixelData = PixelData::create(2, 2, 1, PF_RGBA8); blackPixelData->setColorAt(Color::Black, 0, 0); blackPixelData->setColorAt(Color::Black, 0, 1); blackPixelData->setColorAt(Color::Black, 1, 0); blackPixelData->setColorAt(Color::Black, 1, 1); SPtr<Texture> blackTexture = Texture::_createPtr(blackPixelData); SPtr<PixelData> whitePixelData = PixelData::create(2, 2, 1, PF_RGBA8); whitePixelData->setColorAt(Color::White, 0, 0); whitePixelData->setColorAt(Color::White, 0, 1); whitePixelData->setColorAt(Color::White, 1, 0); whitePixelData->setColorAt(Color::White, 1, 1); SPtr<Texture> whiteTexture = Texture::_createPtr(whitePixelData); SPtr<PixelData> normalPixelData = PixelData::create(2, 2, 1, PF_RGBA8); Color encodedNormal(0.5f, 0.5f, 1.0f); normalPixelData->setColorAt(encodedNormal, 0, 0); normalPixelData->setColorAt(encodedNormal, 0, 1); normalPixelData->setColorAt(encodedNormal, 1, 0); normalPixelData->setColorAt(encodedNormal, 1, 1); SPtr<Texture> normalTexture = Texture::_createPtr(normalPixelData); // Save all textures Path outputDir = mBuiltinDataFolder + TEXTURE_FOLDER; auto saveTexture = [&](const Path& path, const SPtr<Texture>& texture, const String& uuid) { HResource textureResource = gResources()._createResourceHandle(texture, UUID(uuid)); gResources().save(textureResource, path, true); mResourceManifest->registerResource(textureResource.getUUID(), path); }; Path whitePath = outputDir + TextureWhiteFile; saveTexture(whitePath, whiteTexture, "1f7d0e3f-d81b-42ee-9d31-cb6c6fc55824"); Path blackPath = outputDir + TextureBlackFile; saveTexture(blackPath, blackTexture, "149a5c05-9570-4915-9dbd-69acf88b865b"); Path normalPath = outputDir + TextureNormalFile; saveTexture(normalPath, normalTexture, "afb29163-1ef0-4440-9cfb-c1ebb3b3d452"); }
void Resources::loadCallback(const Path& filePath, HResource& resource) { SPtr<Resource> rawResource = loadFromDiskAndDeserialize(filePath); { Lock lock(mInProgressResourcesMutex); // Check if all my dependencies are loaded ResourceLoadData* myLoadData = mInProgressResources[resource.getUUID()]; myLoadData->loadedData = rawResource; myLoadData->remainingDependencies--; } loadComplete(resource); }
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); }
void ResourceListenerManager::onResourceLoaded(const HResource& resource) { RecursiveLock lock(mMutex); mLoadedResources[resource.getUUID()] = resource; }
void BuiltinResources::generateMeshes() { SPtr<VertexDataDesc> vertexDesc = bs_shared_ptr_new<VertexDataDesc>(); vertexDesc->addVertElem(VET_FLOAT3, VES_POSITION); vertexDesc->addVertElem(VET_FLOAT2, VES_TEXCOORD); vertexDesc->addVertElem(VET_FLOAT3, VES_NORMAL); vertexDesc->addVertElem(VET_FLOAT4, VES_TANGENT); vertexDesc->addVertElem(VET_COLOR, VES_COLOR); UINT32 boxNumVertices = 0; UINT32 boxNumIndices = 0; ShapeMeshes3D::getNumElementsAABox(boxNumVertices, boxNumIndices); SPtr<MeshData> boxMeshData = MeshData::create(boxNumVertices, boxNumIndices, vertexDesc); AABox box(Vector3(-0.5f, -0.5f, -0.5f), Vector3(0.5f, 0.5f, 0.5f)); ShapeMeshes3D::solidAABox(box, boxMeshData, 0, 0); SPtr<Mesh> boxMesh = Mesh::_createPtr(RendererMeshData::convert(boxMeshData)); UINT32 sphereNumVertices = 0; UINT32 sphereNumIndices = 0; ShapeMeshes3D::getNumElementsSphere(3, sphereNumVertices, sphereNumIndices); SPtr<MeshData> sphereMeshData = bs_shared_ptr_new<MeshData>(sphereNumVertices, sphereNumIndices, vertexDesc); ShapeMeshes3D::solidSphere(Sphere(Vector3::ZERO, 1.0f), sphereMeshData, 0, 0, 3); SPtr<Mesh> sphereMesh = Mesh::_createPtr(RendererMeshData::convert(sphereMeshData)); UINT32 coneNumVertices = 0; UINT32 coneNumIndices = 0; ShapeMeshes3D::getNumElementsCone(10, coneNumVertices, coneNumIndices); SPtr<MeshData> coneMeshData = bs_shared_ptr_new<MeshData>(coneNumVertices, coneNumIndices, vertexDesc); ShapeMeshes3D::solidCone(Vector3::ZERO, Vector3::UNIT_Y, 1.0f, 1.0f, Vector2::ONE, coneMeshData, 0, 0); SPtr<Mesh> coneMesh = Mesh::_createPtr(RendererMeshData::convert(coneMeshData)); UINT32 cylinderNumVertices = 0; UINT32 cylinderNumIndices = 0; ShapeMeshes3D::getNumElementsCylinder(10, cylinderNumVertices, cylinderNumIndices); SPtr<MeshData> cylinderMeshData = bs_shared_ptr_new<MeshData>(cylinderNumVertices, cylinderNumIndices, vertexDesc); ShapeMeshes3D::solidCylinder(Vector3::ZERO, Vector3::UNIT_Y, 1.0f, 1.0f, Vector2::ONE, cylinderMeshData, 0, 0); SPtr<Mesh> cylinderMesh = Mesh::_createPtr(RendererMeshData::convert(cylinderMeshData)); UINT32 quadNumVertices = 8; UINT32 quadNumIndices = 12; ShapeMeshes3D::getNumElementsQuad(quadNumVertices, quadNumIndices); SPtr<MeshData> quadMeshData = bs_shared_ptr_new<MeshData>(quadNumVertices, quadNumIndices, vertexDesc); std::array<Vector3, 2> axes = {{ Vector3::UNIT_X, Vector3::UNIT_Z }}; std::array<float, 2> sizes = {{ 1.0f, 1.0f }}; Rect3 rect(Vector3::ZERO, axes, sizes); ShapeMeshes3D::solidQuad(rect, quadMeshData, 0, 0); SPtr<Mesh> quadMesh = Mesh::_createPtr(RendererMeshData::convert(quadMeshData)); UINT32 discNumVertices = 0; UINT32 discNumIndices = 0; ShapeMeshes3D::getNumElementsDisc(10, discNumVertices, discNumIndices); SPtr<MeshData> discMeshData = bs_shared_ptr_new<MeshData>(discNumVertices, discNumIndices, vertexDesc); ShapeMeshes3D::solidDisc(Vector3::ZERO, 1.0f, Vector3::UNIT_Y, discMeshData, 0, 0); SPtr<Mesh> discMesh = Mesh::_createPtr(RendererMeshData::convert(discMeshData)); // Save all meshes Path outputDir = mEngineMeshFolder; auto saveMesh = [&](const Path& path, const SPtr<Mesh>& mesh, const String& uuid) { HResource meshResource = gResources()._createResourceHandle(mesh, UUID(uuid)); gResources().save(meshResource, path, true); mResourceManifest->registerResource(meshResource.getUUID(), path); }; Path boxPath = outputDir + MeshBoxFile; saveMesh(boxPath, boxMesh, "bc1d20ca-7fe6-489b-8b5c-dbf798badc95"); Path spherePath = outputDir + MeshSphereFile; saveMesh(spherePath, sphereMesh, "040642f3-04d6-419e-9dba-f7824161c205"); Path conePath = outputDir + MeshConeFile; saveMesh(conePath, coneMesh, "b8cf6db5-1736-47ac-852f-82ecd88b4d46"); Path cylinderPath = outputDir + MeshCylinderFile; saveMesh(cylinderPath, cylinderMesh, "e6b2b797-4e72-7e49-61ba-4e7275bd561d"); Path quadPath = outputDir + MeshQuadFile; saveMesh(quadPath, quadMesh, "06592bf3-f82a-472e-a034-26a98225fbe1"); Path discPath = outputDir + MeshDiscFile; saveMesh(discPath, discMesh, "6f496313-344a-495c-83e8-152e3053c52d"); }
void BuiltinResources::preprocess(bool forceImport, time_t lastUpdateTime) { // Hidden dependency: Textures need to be generated before shaders as they may use the default textures generateTextures(); Path dataListsFilePath = mBuiltinRawDataFolder + DATA_LIST_JSON; SPtr<DataStream> dataListStream = FileSystem::openFile(dataListsFilePath); json dataListJSON = json::parse(dataListStream->getAsString().c_str()); json skinJSON = dataListJSON["Skin"]; json cursorsJSON = dataListJSON["Cursors"]; json iconsJSON = dataListJSON["Icons"]; json includesJSON = dataListJSON["Includes"]; json shadersJSON = dataListJSON["Shaders"]; Path rawSkinFolder = mBuiltinRawDataFolder + SKIN_FOLDER; Path rawCursorFolder = mBuiltinRawDataFolder + CURSOR_FOLDER; Path rawIconFolder = mBuiltinRawDataFolder + ICON_FOLDER; Path rawShaderFolder = mBuiltinRawDataFolder + SHADER_FOLDER; Path rawShaderIncludeFolder = mBuiltinRawDataFolder + SHADER_INCLUDE_FOLDER; // Update DataList.json if needed bool updatedDataLists = false; updatedDataLists |= BuiltinResourcesHelper::updateJSON( rawCursorFolder, BuiltinResourcesHelper::AssetType::Normal, cursorsJSON); updatedDataLists |= BuiltinResourcesHelper::updateJSON( rawIconFolder, BuiltinResourcesHelper::AssetType::Normal, iconsJSON); updatedDataLists |= BuiltinResourcesHelper::updateJSON( rawShaderIncludeFolder, BuiltinResourcesHelper::AssetType::Normal, includesJSON); updatedDataLists |= BuiltinResourcesHelper::updateJSON( rawShaderFolder, BuiltinResourcesHelper::AssetType::Normal, shadersJSON); updatedDataLists |= BuiltinResourcesHelper::updateJSON( rawSkinFolder, BuiltinResourcesHelper::AssetType::Sprite, skinJSON); dataListStream->close(); if(updatedDataLists) { FileSystem::remove(dataListsFilePath); dataListJSON["Skin"] = skinJSON; dataListJSON["Cursors"] = cursorsJSON; dataListJSON["Icons"] = iconsJSON; dataListJSON["Includes"] = includesJSON; dataListJSON["Shaders"] = shadersJSON; String jsonString = dataListJSON.dump(4).c_str(); dataListStream = FileSystem::createAndOpenFile(dataListsFilePath); dataListStream->writeString(jsonString); dataListStream->close(); } Path skinFolder = mBuiltinDataFolder + SKIN_FOLDER; Path iconFolder = mBuiltinDataFolder + ICON_FOLDER; Path shaderIncludeFolder = mBuiltinDataFolder + SHADER_INCLUDE_FOLDER; Path shaderDependenciesFile = mBuiltinDataFolder + "ShaderDependencies.json"; // If forcing import, clear all data folders since everything will be recreated anyway if(forceImport) { FileSystem::remove(mEngineCursorFolder); FileSystem::remove(iconFolder); FileSystem::remove(shaderIncludeFolder); FileSystem::remove(mEngineShaderFolder); FileSystem::remove(skinFolder); FileSystem::remove(shaderDependenciesFile); } // Read shader dependencies JSON json shaderDependenciesJSON; if(FileSystem::exists(shaderDependenciesFile)) { SPtr<DataStream> stream = FileSystem::openFile(shaderDependenciesFile); shaderDependenciesJSON = json::parse(stream->getAsString().c_str()); stream->close(); } // Import cursors { BuiltinResourcesHelper::updateManifest( mEngineCursorFolder, cursorsJSON, mResourceManifest, BuiltinResourcesHelper::AssetType::Normal); Vector<bool> importFlags = BuiltinResourcesHelper::generateImportFlags( cursorsJSON, rawCursorFolder, lastUpdateTime, forceImport); BuiltinResourcesHelper::importAssets( cursorsJSON, importFlags, rawCursorFolder, mEngineCursorFolder, mResourceManifest, BuiltinResourcesHelper::AssetType::Normal); } // Import icons { BuiltinResourcesHelper::updateManifest( iconFolder, iconsJSON, mResourceManifest, BuiltinResourcesHelper::AssetType::Normal); Vector<bool> importFlags = BuiltinResourcesHelper::generateImportFlags( iconsJSON, rawIconFolder, lastUpdateTime, forceImport); BuiltinResourcesHelper::importAssets( iconsJSON, importFlags, rawIconFolder, iconFolder, mResourceManifest, BuiltinResourcesHelper::AssetType::Normal); } // Import shaders { BuiltinResourcesHelper::updateManifest( shaderIncludeFolder, includesJSON, mResourceManifest, BuiltinResourcesHelper::AssetType::Normal); BuiltinResourcesHelper::updateManifest( mEngineShaderFolder, shadersJSON, mResourceManifest, BuiltinResourcesHelper::AssetType::Normal); Vector<bool> includeImportFlags = BuiltinResourcesHelper::generateImportFlags( includesJSON, rawShaderIncludeFolder, lastUpdateTime, forceImport); Vector<bool> shaderImportFlags = BuiltinResourcesHelper::generateImportFlags( shadersJSON, rawShaderFolder, lastUpdateTime, forceImport, &shaderDependenciesJSON, rawShaderIncludeFolder); // Hidden dependency: Includes must be imported before shaders, but import flags for shaders must be generated // before includes are imported, since the process checks if imports changed BuiltinResourcesHelper::importAssets( includesJSON, includeImportFlags, rawShaderIncludeFolder, shaderIncludeFolder, mResourceManifest, BuiltinResourcesHelper::AssetType::Normal, nullptr, true); BuiltinResourcesHelper::importAssets( shadersJSON, shaderImportFlags, rawShaderFolder, mEngineShaderFolder, mResourceManifest, BuiltinResourcesHelper::AssetType::Normal, &shaderDependenciesJSON, true); } // Import GUI sprites { BuiltinResourcesHelper::updateManifest( skinFolder, skinJSON, mResourceManifest, BuiltinResourcesHelper::AssetType::Sprite); Vector<bool> skinImportFlags = BuiltinResourcesHelper::generateImportFlags( skinJSON, rawSkinFolder, lastUpdateTime, forceImport); BuiltinResourcesHelper::importAssets( skinJSON, skinImportFlags, rawSkinFolder, skinFolder, mResourceManifest, BuiltinResourcesHelper::AssetType::Sprite); } // Update shader dependencies JSON { String jsonString = shaderDependenciesJSON.dump(4).c_str(); dataListStream = FileSystem::createAndOpenFile(shaderDependenciesFile); dataListStream->writeString(jsonString); dataListStream->close(); } // Import font BuiltinResourcesHelper::importFont( mBuiltinRawDataFolder + DefaultFontFilename, DefaultFontFilename, mBuiltinDataFolder, { DefaultFontSize }, false, UUID("c9f08cab-f9c9-47c4-96e0-1066a8d4455b"), mResourceManifest); // Generate & save GUI skin { SPtr<GUISkin> skin = generateGUISkin(); Path outputPath = mBuiltinDataFolder + (GUISkinFile + u8".asset"); HResource skinResource = gResources()._createResourceHandle(skin, UUID("c1bf9a9d-4355-4841-a538-25e67730ec4b")); gResources().save(skinResource, outputPath, true); mResourceManifest->registerResource(skinResource.getUUID(), outputPath); } // Generate & save meshes generateMeshes(); Resources::instance().unloadAllUnused(); }
void BuiltinEditorResources::preprocess(bool forceImport, time_t lastUpdateTime) { Resources::instance().unloadAllUnused(); Path dataListsFilePath = BuiltinRawDataFolder + DATA_LIST_JSON; SPtr<DataStream> dataListStream = FileSystem::openFile(dataListsFilePath); json dataListJSON = json::parse(dataListStream->getAsString().c_str()); json skinJSON = dataListJSON["Skin"]; json iconsJSON = dataListJSON["Icons"]; json includesJSON = dataListJSON["Includes"]; json shadersJSON = dataListJSON["Shaders"]; // Update DataList.json if needed bool updatedDataLists = false; updatedDataLists |= BuiltinResourcesHelper::updateJSON( EditorRawIconsFolder, BuiltinResourcesHelper::AssetType::Sprite, iconsJSON); updatedDataLists |= BuiltinResourcesHelper::updateJSON( EditorRawShaderIncludeFolder, BuiltinResourcesHelper::AssetType::Normal, includesJSON); updatedDataLists |= BuiltinResourcesHelper::updateJSON( EditorRawShaderFolder, BuiltinResourcesHelper::AssetType::Normal, shadersJSON); updatedDataLists |= BuiltinResourcesHelper::updateJSON( EditorRawSkinFolder, BuiltinResourcesHelper::AssetType::Sprite, skinJSON); dataListStream->close(); if (updatedDataLists) { FileSystem::remove(dataListsFilePath); dataListJSON["Skin"] = skinJSON; dataListJSON["Icons"] = iconsJSON; dataListJSON["Includes"] = includesJSON; dataListJSON["Shaders"] = shadersJSON; String jsonString = dataListJSON.dump(4).c_str(); dataListStream = FileSystem::createAndOpenFile(dataListsFilePath); dataListStream->writeString(jsonString); dataListStream->close(); } // If forcing import, clear all data folders since everything will be recreated anyway Path shaderDependenciesFile = BuiltinDataFolder + "ShaderDependencies.json"; if(forceImport) { FileSystem::remove(EditorIconFolder); FileSystem::remove(EditorShaderIncludeFolder); FileSystem::remove(EditorShaderFolder); FileSystem::remove(EditorSkinFolder); FileSystem::remove(shaderDependenciesFile); } // Read shader dependencies JSON json shaderDependenciesJSON; if(FileSystem::exists(shaderDependenciesFile)) { SPtr<DataStream> stream = FileSystem::openFile(shaderDependenciesFile); shaderDependenciesJSON = json::parse(stream->getAsString().c_str()); stream->close(); } // Import icons { BuiltinResourcesHelper::updateManifest( EditorIconFolder, iconsJSON, mResourceManifest, BuiltinResourcesHelper::AssetType::Sprite); Vector<bool> importFlags = BuiltinResourcesHelper::generateImportFlags( iconsJSON, EditorRawIconsFolder, lastUpdateTime, forceImport); BuiltinResourcesHelper::importAssets( iconsJSON, importFlags, EditorRawIconsFolder, EditorIconFolder, mResourceManifest, BuiltinResourcesHelper::AssetType::Sprite); } // Import shaders { BuiltinResourcesHelper::updateManifest( EditorShaderIncludeFolder, includesJSON, mResourceManifest, BuiltinResourcesHelper::AssetType::Normal); BuiltinResourcesHelper::updateManifest( EditorShaderFolder, shadersJSON, mResourceManifest, BuiltinResourcesHelper::AssetType::Normal); Vector<bool> includeImportFlags = BuiltinResourcesHelper::generateImportFlags( includesJSON, EditorRawShaderIncludeFolder, lastUpdateTime, forceImport); Vector<bool> shaderImportFlags = BuiltinResourcesHelper::generateImportFlags( shadersJSON, EditorRawShaderFolder, lastUpdateTime, forceImport, &shaderDependenciesJSON, EditorRawShaderIncludeFolder); // Hidden dependency: Includes must be imported before shaders, but import flags for shaders must be generated // before includes are imported, since the process checks if imports changed BuiltinResourcesHelper::importAssets( includesJSON, includeImportFlags, EditorRawShaderIncludeFolder, EditorShaderIncludeFolder, mResourceManifest, BuiltinResourcesHelper::AssetType::Normal); BuiltinResourcesHelper::importAssets( shadersJSON, shaderImportFlags, EditorRawShaderFolder, EditorShaderFolder, mResourceManifest, BuiltinResourcesHelper::AssetType::Normal, &shaderDependenciesJSON); } // Import GUI sprites { BuiltinResourcesHelper::updateManifest( EditorSkinFolder, skinJSON, mResourceManifest, BuiltinResourcesHelper::AssetType::Sprite); Vector<bool> includeImportFlags = BuiltinResourcesHelper::generateImportFlags( skinJSON, EditorRawSkinFolder, lastUpdateTime, forceImport); BuiltinResourcesHelper::importAssets( skinJSON, includeImportFlags, EditorRawSkinFolder, EditorSkinFolder, mResourceManifest, BuiltinResourcesHelper::AssetType::Sprite); } // Update shader dependencies JSON { String jsonString = shaderDependenciesJSON.dump(4).c_str(); dataListStream = FileSystem::createAndOpenFile(shaderDependenciesFile); dataListStream->writeString(jsonString); dataListStream->close(); } // Import fonts BuiltinResourcesHelper::importFont( BuiltinRawDataFolder + DefaultFontFilename, DefaultFontFilename, BuiltinDataFolder, { DefaultFontSize }, true, UUID("6ce69053-00d7-4c60-a229-249b8d8fd60e"), mResourceManifest); BuiltinResourcesHelper::importFont( BuiltinRawDataFolder + DefaultFontFilename, DefaultAAFontFilename, BuiltinDataFolder, { TitleFontSize }, true, UUID("10999b74-d976-4116-9f72-21e489a7a8e4"), mResourceManifest); // Import splash screen { Path inputPath = BuiltinRawDataFolder + String(SplashScreenName); Path outputPath = BuiltinDataFolder + (String(SplashScreenName) + ".asset"); auto textureIO = gImporter().createImportOptions<TextureImportOptions>(inputPath); textureIO->setCPUCached(true); textureIO->setGenerateMipmaps(false); HTexture splashTexture = gImporter().import<Texture>(inputPath, textureIO); SPtr<PixelData> splashPixelData = splashTexture->getProperties().allocBuffer(0, 0); splashTexture->readCachedData(*splashPixelData); FileEncoder fe(outputPath); fe.encode(splashPixelData.get()); } // Generate & save GUI skin { SPtr<GUISkin> skin = generateGUISkin(); Path outputPath = BuiltinDataFolder + (GUISkinFile + ".asset"); HResource skinResource = gResources()._createResourceHandle(skin, UUID("ec0ea68d-efa5-4a3b-a6fc-b15aaec9689f")); gResources().save(skinResource, outputPath, true); mResourceManifest->registerResource(skinResource.getUUID(), outputPath); } Resources::instance().unloadAllUnused(); }
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; }
void Resources::loadComplete(HResource& resource) { String uuid = resource.getUUID(); ResourceLoadData* myLoadData = nullptr; bool finishLoad = true; Vector<ResourceLoadData*> dependantLoads; { Lock inProgresslock(mInProgressResourcesMutex); auto iterFind = mInProgressResources.find(uuid); if (iterFind != mInProgressResources.end()) { myLoadData = iterFind->second; finishLoad = myLoadData->remainingDependencies == 0; if (finishLoad) mInProgressResources.erase(iterFind); } auto iterFind2 = mDependantLoads.find(uuid); if (iterFind2 != mDependantLoads.end()) dependantLoads = iterFind2->second; if (finishLoad) { mDependantLoads.erase(uuid); // If loadedData is null then we're probably completing load on an already loaded resource, triggered // by its dependencies. if (myLoadData != nullptr && myLoadData->loadedData != nullptr) { Lock loadedLock(mLoadedResourceMutex); mLoadedResources[uuid] = myLoadData->resData; resource.setHandleData(myLoadData->loadedData, uuid); } for (auto& dependantLoad : dependantLoads) dependantLoad->remainingDependencies--; } } for (auto& dependantLoad : dependantLoads) { HResource dependant = dependantLoad->resData.resource.lock(); loadComplete(dependant); } if (finishLoad && myLoadData != nullptr) { onResourceLoaded(resource); // This should only ever be true on the main thread if (myLoadData->notifyImmediately) ResourceListenerManager::instance().notifyListeners(uuid); bs_delete(myLoadData); } }