/// Recursive function for resolving a package request. /// /// @param[out] rspPackage Resolved package. /// @param[in] packagePath Package object path. void CachePackageLoader::ResolvePackage( AssetPtr& rspPackage, AssetPath packagePath ) { HELIUM_ASSERT( !packagePath.IsEmpty() ); rspPackage = Asset::FindObject( packagePath ); if( !rspPackage ) { AssetPtr spParent; AssetPath parentPath = packagePath.GetParent(); if( !parentPath.IsEmpty() ) { ResolvePackage( spParent, parentPath ); HELIUM_ASSERT( spParent ); } HELIUM_VERIFY( Asset::CreateObject( rspPackage, Package::GetStaticType(), packagePath.GetName(), spParent ) ); HELIUM_ASSERT( rspPackage ); HELIUM_ASSERT( rspPackage->IsA( Package::GetStaticType()->GetMetaClass() ) ); } rspPackage->SetFlags( Asset::FLAG_PRELOADED | Asset::FLAG_LINKED | Asset::FLAG_LOADED ); }
/// Find an object based on its path name. /// /// @param[in] path FilePath of the object to locate. /// /// @return Pointer to the object if found, null pointer if not found. Asset* Asset::FindObject( AssetPath path ) { // Make sure the path isn't empty. if( path.IsEmpty() ) { return NULL; } // Assemble a list of object names and instance indices, from the top level on down. size_t pathDepth = 0; size_t packageDepth = 0; for( AssetPath testPath = path; !testPath.IsEmpty(); testPath = testPath.GetParent() ) { ++pathDepth; if( testPath.IsPackage() ) { ++packageDepth; } } StackMemoryHeap<>& rStackHeap = ThreadLocalStackAllocator::GetMemoryHeap(); StackMemoryHeap<>::Marker stackMarker( rStackHeap ); Name* pPathNames = static_cast< Name* >( rStackHeap.Allocate( sizeof( Name ) * pathDepth ) ); HELIUM_ASSERT( pPathNames ); uint32_t* pInstanceIndices = static_cast< uint32_t* >( rStackHeap.Allocate( sizeof( uint32_t ) * pathDepth ) ); HELIUM_ASSERT( pInstanceIndices ); size_t pathIndex = pathDepth; for( AssetPath testPath = path; !testPath.IsEmpty(); testPath = testPath.GetParent() ) { HELIUM_ASSERT( pathIndex != 0 ); --pathIndex; pPathNames[ pathIndex ] = testPath.GetName(); pInstanceIndices[ pathIndex ] = testPath.GetInstanceIndex(); } HELIUM_ASSERT( pathIndex == 0 ); // Search from the root. return FindChildOf( NULL, pPathNames, pInstanceIndices, pathDepth, packageDepth ); }
int64_t AssetLoader::GetAssetFileTimestamp( const AssetPath &path ) { Package *pPackage = Asset::Find<Package>( path.GetParent() ); HELIUM_ASSERT( pPackage ); PackageLoader *pLoader = pPackage->GetLoader(); HELIUM_ASSERT( pLoader ); return pLoader->GetAssetFileSystemTimestamp( path ); }
/// Initialize this manager. /// /// @return True if this manager was initialized successfully, false if not. /// /// @see Shutdown() bool WorldManager::Initialize() { HELIUM_ASSERT( !m_spRootSceneDefinitionsPackage ); // Create the world package first. // XXX TMC: Note that we currently assume that the world package has no parents, so we don't need to handle // recursive package creation. If we want to move the world package to a subpackage, this will need to be // updated accordingly. AssetPath rootSceneDefinitionsPackagePath = GetRootSceneDefinitionPackagePath(); HELIUM_ASSERT( !rootSceneDefinitionsPackagePath.IsEmpty() ); HELIUM_ASSERT( rootSceneDefinitionsPackagePath.GetParent().IsEmpty() ); bool bCreateResult = Asset::Create< Package >( m_spRootSceneDefinitionsPackage, rootSceneDefinitionsPackagePath.GetName(), NULL ); HELIUM_ASSERT( bCreateResult ); if( !bCreateResult ) { HELIUM_TRACE( TraceLevels::Error, TXT( "WorldManager::Initialize(): Failed to create world definition package \"%s\".\n" ), *rootSceneDefinitionsPackagePath.ToString() ); return false; } HELIUM_ASSERT( m_spRootSceneDefinitionsPackage ); // Reset frame timings. m_actualFrameTickCount = 0; m_frameTickCount = 0; m_frameDeltaTickCount = 0; m_frameDeltaSeconds = 0.0f; // First frame still needs to be processed. m_bProcessedFirstFrame = false; return true; }
/// @copydoc PackageLoader::BeginLoadObject() size_t LoosePackageLoader::BeginLoadObject( AssetPath path, Reflect::ObjectResolver *pResolver, bool forceReload ) { HELIUM_TRACE( TraceLevels::Info, TXT(" LoosePackageLoader::BeginLoadObject - Loading path %s\n"), *path.ToString() ); HELIUM_TRACE( TraceLevels::Debug, TXT( "LoosePackageLoader::BeginLoadObject: Beginning load for path \"%s\".\n"), *path.ToString()); HELIUM_TRACE( TraceLevels::Debug, TXT( "LoosePackageLoader::BeginLoadObject: Beginning load for path \"%s\". pResolver = %x\n"), *path.ToString(), pResolver); // Make sure preloading has completed. HELIUM_ASSERT( m_preloadedCounter != 0 ); if( !m_preloadedCounter ) { return Invalid< size_t >(); } // If this package is requested, simply provide the (already loaded) package instance. if( path == m_packagePath ) { LoadRequest* pRequest = m_loadRequestPool.Allocate(); HELIUM_ASSERT( pRequest ); HELIUM_ASSERT( m_spPackage ); pRequest->spObject = m_spPackage.Ptr(); SetInvalid( pRequest->index ); HELIUM_ASSERT( !pRequest->spType ); HELIUM_ASSERT( !pRequest->spTemplate ); HELIUM_ASSERT( !pRequest->spOwner ); SetInvalid( pRequest->templateLoadId ); SetInvalid( pRequest->ownerLoadId ); SetInvalid( pRequest->persistentResourceDataLoadId ); pRequest->pCachedObjectDataBuffer = NULL; pRequest->cachedObjectDataBufferSize = 0; SetInvalid( pRequest->asyncFileLoadId ); pRequest->pAsyncFileLoadBuffer = NULL; pRequest->asyncFileLoadBufferSize = 0; pRequest->pResolver = NULL; pRequest->forceReload = forceReload; pRequest->flags = LOAD_FLAG_PRELOADED; size_t requestId = m_loadRequests.Add( pRequest ); return requestId; } size_t objectIndex = FindObjectByPath( path ); size_t objectCount = GetObjectCount(); if( objectIndex >= objectCount ) { HELIUM_TRACE( TraceLevels::Error, TXT( "LoosePackageLoader::BeginLoadObject(): Failed to locate \"%s\" for loading. Verify the file exists.\n" ), *path.ToString() ); return Invalid< size_t >(); } SerializedObjectData& rObjectData = m_objects[ objectIndex ]; // Verify that the metadata was read successfully if( !rObjectData.bMetadataGood ) { HELIUM_TRACE( TraceLevels::Error, TXT( "LoosePackageLoader::BeginLoadObject(): Failed to read metadata for object \"%s\" during PackagePreload. Search log for parsing errors.\n" ), *path.ToString() ); return Invalid< size_t >(); } // Locate the type object. HELIUM_ASSERT( !rObjectData.typeName.IsEmpty() ); AssetType* pType = AssetType::Find( rObjectData.typeName ); if( !pType ) { HELIUM_TRACE( TraceLevels::Error, TXT( "LoosePackageLoader::BeginLoadObject(): Failed to locate type \"%s\" for loading object \"%s\".\n" ), *rObjectData.typeName, *path.ToString() ); HELIUM_TRACE( TraceLevels::Info, TXT( "Current registered types:\n" ) ); for ( AssetType::ConstIterator iter = AssetType::GetTypeBegin(); iter != AssetType::GetTypeEnd(); ++iter) { HELIUM_TRACE( TraceLevels::Info, TXT( " - %s\n" ), *iter->GetName() ); } return Invalid< size_t >(); } #ifndef NDEBUG size_t loadRequestSize = m_loadRequests.GetSize(); for( size_t loadRequestIndex = 0; loadRequestIndex < loadRequestSize; ++loadRequestIndex ) { if( !m_loadRequests.IsElementValid( loadRequestIndex ) ) { continue; } LoadRequest* pRequest = m_loadRequests[ loadRequestIndex ]; HELIUM_ASSERT( pRequest ); HELIUM_ASSERT( pRequest->index != objectIndex ); if( pRequest->index == objectIndex ) { return Invalid< size_t >(); } } #endif LoadRequest* pRequest = m_loadRequestPool.Allocate(); HELIUM_ASSERT( pRequest ); HELIUM_ASSERT( !pRequest->spObject ); pRequest->index = objectIndex; pRequest->spType = pType; HELIUM_ASSERT( !pRequest->spTemplate ); HELIUM_ASSERT( !pRequest->spOwner ); SetInvalid( pRequest->templateLoadId ); SetInvalid( pRequest->ownerLoadId ); SetInvalid( pRequest->persistentResourceDataLoadId ); pRequest->pCachedObjectDataBuffer = NULL; pRequest->cachedObjectDataBufferSize = 0; SetInvalid( pRequest->asyncFileLoadId ); pRequest->pAsyncFileLoadBuffer = NULL; pRequest->asyncFileLoadBufferSize = 0; pRequest->pResolver = pResolver; pRequest->forceReload = forceReload; pRequest->flags = 0; // If a fully-loaded object already exists with the same name, do not attempt to re-load the object (just mark // the request as complete). if ( !forceReload ) { pRequest->spObject = Asset::FindObject( path ); } Asset* pObject = pRequest->spObject; if( pObject && pObject->IsFullyLoaded() ) { pRequest->flags = LOAD_FLAG_PRELOADED; } else { HELIUM_ASSERT( !pObject || !pObject->GetAnyFlagSet( Asset::FLAG_LOADED | Asset::FLAG_LINKED ) ); // Begin loading the template and owner objects. Note that there isn't much reason to check for failure // until we tick this request, as we need to make sure any other load requests for the template/owner that // did succeed are properly synced anyway. AssetLoader* pAssetLoader = AssetLoader::GetStaticInstance(); HELIUM_ASSERT( pAssetLoader ); if( rObjectData.templatePath.IsEmpty() ) { // Make sure the template is fully loaded. Asset* pTemplate = pType->GetTemplate(); rObjectData.templatePath = pTemplate->GetPath(); if( pTemplate->IsFullyLoaded() ) { pRequest->spTemplate = pTemplate; } else { pRequest->templateLoadId = pAssetLoader->BeginLoadObject( rObjectData.templatePath ); } } else { pRequest->templateLoadId = pAssetLoader->BeginLoadObject( rObjectData.templatePath ); } AssetPath ownerPath = path.GetParent(); if( ownerPath == m_packagePath ) { // Easy check: if the owner is this package (which is likely), we don't need to load it. pRequest->spOwner = m_spPackage.Ptr(); } else if( !ownerPath.IsEmpty() ) { pRequest->ownerLoadId = pAssetLoader->BeginLoadObject( ownerPath ); } } size_t requestId = m_loadRequests.Add( pRequest ); return requestId; }
/// Get the package loader to use to load the object with the given path, creating it if necessary. /// /// @param[in] path Asset path. /// /// @return Package loader to use to load the specified object. LoosePackageLoader* LoosePackageLoaderMap::GetPackageLoader( AssetPath path ) { HELIUM_ASSERT( !path.IsEmpty() ); // Resolve the object's package. AssetPath packagePath = path; while( !packagePath.IsPackage() ) { packagePath = packagePath.GetParent(); if( packagePath.IsEmpty() ) { HELIUM_TRACE( TraceLevels::Error, ( TXT( "LoosePackageLoaderMap::GetPackageLoader(): Cannot resolve package loader for \"%s\", as it " ) TXT( "is not located in a package.\n" ) ), *path.ToString() ); return NULL; } } // Locate an existing package loader. ConcurrentHashMap< AssetPath, LoosePackageLoader* >::ConstAccessor constMapAccessor; if( m_packageLoaderMap.Find( constMapAccessor, packagePath ) ) { LoosePackageLoader* pLoader = constMapAccessor->Second(); HELIUM_ASSERT( pLoader ); return pLoader; } // Add a new package loader entry. ConcurrentHashMap< AssetPath, LoosePackageLoader* >::Accessor mapAccessor; bool bInserted = m_packageLoaderMap.Insert( mapAccessor, KeyValue< AssetPath, LoosePackageLoader* >( packagePath, NULL ) ); if( bInserted ) { // Entry added, so create and initialize the package loader. LoosePackageLoader* pLoader = new LoosePackageLoader; HELIUM_ASSERT( pLoader ); bool bInitResult = pLoader->Initialize( packagePath ); HELIUM_ASSERT( bInitResult ); if( !bInitResult ) { HELIUM_TRACE( TraceLevels::Error, TXT( "LoosePackageLoaderMap::GetPackageLoader(): Failed to initialize package loader for \"%s\".\n" ), *packagePath.ToString() ); m_packageLoaderMap.Remove( mapAccessor ); return NULL; } HELIUM_VERIFY( pLoader->BeginPreload() ); mapAccessor->Second() = pLoader; } // PMD: I am not 100% sure this is all thread safe. I think it could break if: // - Thread 1 inserts a key/value of path and NULL, as above // - Thread 2 returns false from the insert so we come straight here // - Thread 2 tries to use pLoader before thread 1 assigns ploader to the value of the key // Note: If we did not do this assert here, we could potentially get nulls from this same thread later when // trying to find pLoader. // // Leaving it alone for now since I'm not sure, but if this assert gets tripped, we need to revisit this. // Easy fix may be to allocate and construct (but don't completely init) an LoosePackageLoader, and try // to insert that directly rather than the null above. If insert succeeds, finish, else ditch our loader // and grab the one out of the array LoosePackageLoader* pLoader = mapAccessor->Second(); HELIUM_ASSERT( pLoader ); return pLoader; }
/// @copydoc AssetLoader::CacheObject() bool LooseAssetLoader::CacheObject( Asset* pAsset, bool bEvictPlatformPreprocessedResourceData ) { HELIUM_ASSERT( pAsset ); HELIUM_TRACE( TraceLevels::Info, TXT( "LooseAssetLoader::CacheObject(): Caching asset %s.\n" ), *pAsset->GetPath().ToString() ); // Don't cache broken objects or packages. if( pAsset->GetAnyFlagSet( Asset::FLAG_BROKEN ) || pAsset->IsPackage() ) { return false; } // Make sure we have an object preprocessor instance with which to cache the object. AssetPreprocessor* pAssetPreprocessor = AssetPreprocessor::GetStaticInstance(); if( !pAssetPreprocessor ) { HELIUM_TRACE( TraceLevels::Warning, TXT( "LooseAssetLoader::CacheObject(): Missing AssetPreprocessor to use for caching.\n" ) ); return false; } // User configuration objects should not be cached. AssetPath objectPath = pAsset->GetPath(); Config& rConfig = Config::GetStaticInstance(); // Only cache the files we care about if ( rConfig.IsAssetPathInUserConfigPackage(objectPath) ) { return false; } int64_t objectTimestamp = pAsset->GetAssetFileTimeStamp(); if( !pAsset->IsDefaultTemplate() ) { Resource* pResource = Reflect::SafeCast< Resource >( pAsset ); if( pResource ) { AssetPath baseResourcePath = pResource->GetPath(); HELIUM_ASSERT( !baseResourcePath.IsPackage() ); for( ; ; ) { AssetPath parentPath = baseResourcePath.GetParent(); if( parentPath.IsEmpty() || parentPath.IsPackage() ) { break; } baseResourcePath = parentPath; } FilePath sourceFilePath; if ( !FileLocations::GetDataDirectory( sourceFilePath ) ) { HELIUM_TRACE( TraceLevels::Warning, TXT( "LooseAssetLoader::CacheObject(): Could not obtain data directory.\n" ) ); return false; } sourceFilePath += baseResourcePath.ToFilePathString().GetData(); Status stat; stat.Read( sourceFilePath.Get().c_str() ); int64_t sourceFileTimestamp = stat.m_ModifiedTime; if( sourceFileTimestamp > objectTimestamp ) { objectTimestamp = sourceFileTimestamp; } } } // Cache the object. bool bSuccess = pAssetPreprocessor->CacheObject( pAsset, objectTimestamp, bEvictPlatformPreprocessedResourceData ); if( !bSuccess ) { HELIUM_TRACE( TraceLevels::Error, TXT( "LooseAssetLoader: Failed to cache object \"%s\".\n" ), *objectPath.ToString() ); } return bSuccess; }
/// Set this path to the combination of two paths. /// /// @param[in] pRootPath Root portion of the path. /// @param[in] subPath Sub-path component. /// /// @return True if the paths could be joined into a valid path (to which this path was set), false if joining was /// invalid. bool AssetPath::Join( const char* pRootPath, AssetPath subPath ) { if( !pRootPath || pRootPath[ 0 ] == TXT( '\0' ) ) { m_pEntry = subPath.m_pEntry; return true; } // Parse the root path into a series of names. StackMemoryHeap<>& rStackHeap = ThreadLocalStackAllocator::GetMemoryHeap(); StackMemoryHeap<>::Marker stackMarker( rStackHeap ); Name* pRootPathNames; uint32_t* pRootPathIndices; size_t rootPathNameCount; size_t rootPathPackageCount; if( !Parse( pRootPath, rStackHeap, pRootPathNames, pRootPathIndices, rootPathNameCount, rootPathPackageCount ) ) { return false; } if( rootPathNameCount != rootPathPackageCount ) { AssetPath testSubPathComponent = subPath.GetParent(); AssetPath subPathComponent; do { subPathComponent = testSubPathComponent; testSubPathComponent = testSubPathComponent.GetParent(); if( subPathComponent.IsPackage() ) { HELIUM_TRACE( TraceLevels::Error, ( TXT( "AssetPath::Join(): Cannot combine \"%s\" and \"%s\" (second path is rooted in a " ) TXT( "package, while the first path ends in an object).\n" ) ), pRootPath, *subPath.ToString() ); return false; } } while( !testSubPathComponent.IsEmpty() ); } // Assemble the list of path names in reverse order for performing the object path lookup/add. size_t nameCount = rootPathNameCount; size_t packageCount = rootPathPackageCount; AssetPath testPath; for( testPath = subPath; !testPath.IsEmpty(); testPath = testPath.GetParent() ) { ++nameCount; if( testPath.IsPackage() ) { ++packageCount; } } Name* pEntryNames = static_cast< Name* >( rStackHeap.Allocate( sizeof( Name ) * nameCount ) ); HELIUM_ASSERT( pEntryNames ); uint32_t* pInstanceIndices = static_cast< uint32_t* >( rStackHeap.Allocate( sizeof( uint32_t ) * nameCount ) ); HELIUM_ASSERT( pInstanceIndices ); Name* pCurrentName = pEntryNames; uint32_t* pCurrentIndex = pInstanceIndices; for( testPath = subPath; !testPath.IsEmpty(); testPath = testPath.GetParent() ) { *pCurrentName = testPath.GetName(); *pCurrentIndex = testPath.GetInstanceIndex(); ++pCurrentName; ++pCurrentIndex; } ArrayCopy( pCurrentName, pRootPathNames, rootPathNameCount ); ArrayCopy( pCurrentIndex, pRootPathIndices, rootPathNameCount ); // Set the path. Set( pEntryNames, pInstanceIndices, nameCount, packageCount ); return true; }