/// Recursive function for resolving a package request. /// /// @param[out] rspPackage Resolved package. /// @param[in] packagePath Package object path. void CachePackageLoader::ResolvePackage( GameObjectPtr& rspPackage, GameObjectPath packagePath ) { HELIUM_ASSERT( !packagePath.IsEmpty() ); rspPackage = GameObject::FindObject( packagePath ); if( !rspPackage ) { GameObjectPtr spParent; GameObjectPath parentPath = packagePath.GetParent(); if( !parentPath.IsEmpty() ) { ResolvePackage( spParent, parentPath ); HELIUM_ASSERT( spParent ); } HELIUM_VERIFY( GameObject::CreateObject( rspPackage, Package::GetStaticType(), packagePath.GetName(), spParent ) ); HELIUM_ASSERT( rspPackage ); HELIUM_ASSERT( rspPackage->IsClass( Package::GetStaticType()->GetClass() ) ); } rspPackage->SetFlags( GameObject::FLAG_PRELOADED | GameObject::FLAG_LINKED | GameObject::FLAG_LOADED ); }
/// Begin asynchronous loading of a shader variant. /// /// @param[in] shaderType Shader type. /// @param[in] userOptionIndex Index associated with the user option combination for the shader variant. /// /// @return ID associated with the load procedure, or an invalid index if the load could not be started. /// /// @see TryFinishLoadVariant() size_t Shader::BeginLoadVariant( RShader::EType shaderType, uint32_t userOptionIndex ) { HELIUM_ASSERT( static_cast< size_t >( shaderType ) < static_cast< size_t >( RShader::TYPE_MAX ) ); // Make sure the user option index is valid. if( userOptionIndex >= m_variantCounts[ shaderType ] ) { HELIUM_TRACE( TRACE_ERROR, ( TXT( "Shader::BeginLoadVariant(): Invalid user option index %" ) TPRIuSZ TXT( " specified for " ) TXT( "variant of shader \"%s\" (only %" ) TPRIuSZ TXT ( " variants are available for shader type %" ) TPRId32 TXT( ").\n" ) ), userOptionIndex, *GetPath().ToString(), m_variantCounts[ shaderType ], static_cast< int32_t >( shaderType ) ); return Invalid< size_t >(); } // Use the begin-load override if one is registered. if( sm_pBeginLoadVariantOverride ) { size_t loadId = sm_pBeginLoadVariantOverride( sm_pVariantLoadOverrideData, this, shaderType, userOptionIndex ); return loadId; } // Build the object path name. tchar_t shaderTypeCharacter; if( shaderType == RShader::TYPE_VERTEX ) { shaderTypeCharacter = TXT( 'v' ); } else { HELIUM_ASSERT( shaderType == RShader::TYPE_PIXEL ); shaderTypeCharacter = TXT( 'p' ); } String variantNameString; variantNameString.Format( TXT( "%c%" ) TPRIu32, shaderTypeCharacter, userOptionIndex ); GameObjectPath variantPath; HELIUM_VERIFY( variantPath.Set( Name( variantNameString ), false, GetPath() ) ); // Begin the load process. GameObjectLoader* pObjectLoader = GameObjectLoader::GetStaticInstance(); HELIUM_ASSERT( pObjectLoader ); size_t loadId = pObjectLoader->BeginLoadObject( variantPath ); return loadId; }
/// Get the path to the package containing all world instances. /// /// @return World package path. GameObjectPath WorldManager::GetWorldPackagePath() const { static GameObjectPath worldPackagePath; if( worldPackagePath.IsEmpty() ) { HELIUM_VERIFY( worldPackagePath.Set( TXT( "/Worlds" ) ) ); } return worldPackagePath; }
/// Save the user configuration settings. /// /// @return True if the configuration was saved successfully, false if not. bool ConfigPc::SaveUserConfig() { HELIUM_TRACE( TRACE_INFO, TXT( "ConfigPc: Saving user configuration.\n" ) ); Config& rConfig = Config::GetStaticInstance(); Package* pConfigPackage = rConfig.GetUserConfigPackage(); if( !pConfigPackage ) { HELIUM_TRACE( TRACE_WARNING, TXT( "ConfigPc: No user configuration exists to save.\n" ) ); return false; } Path userDataDirectory; if ( !File::GetUserDataDirectory( userDataDirectory ) ) { HELIUM_TRACE( TRACE_WARNING, TXT( "ConfigPc: No user data directory could be determined.\n" ) ); return false; } GameObjectPath configPackagePath = pConfigPackage->GetPath(); Path packageFilePath( userDataDirectory + configPackagePath.ToFilePathString().GetData() + HELIUM_XML_PACKAGE_FILE_EXTENSION ); HELIUM_TRACE( TRACE_INFO, TXT( "ConfigPc: Saving configuration to \"%s\".\n" ), *packageFilePath ); XmlSerializer serializer; if( !serializer.Initialize( packageFilePath.c_str() ) ) { HELIUM_TRACE( TRACE_ERROR, TXT( "ConfigPc: Failed to initialize package serializer for writing to \"%s\".\n" ), *packageFilePath ); return false; } for( GameObject* pConfigObject = pConfigPackage->GetFirstChild(); pConfigObject != NULL; pConfigObject = pConfigObject->GetNextSibling() ) { if( !pConfigObject->IsPackage() ) { RecursiveSerializeObject( serializer, pConfigObject ); } } serializer.Shutdown(); HELIUM_TRACE( TRACE_INFO, TXT( "ConfigPc: User configuration saved.\n" ) ); return true; }
/// Begin asynchronous loading of an object. /// /// @param[in] path GameObject path. /// /// @return ID for the load request if started successfully, invalid index if not. /// /// @see TryFinishLoad(), FinishLoad() size_t GameObjectLoader::BeginLoadObject( GameObjectPath path ) { // Search for an existing load request with the given path. ConcurrentHashMap< GameObjectPath, LoadRequest* >::ConstAccessor requestConstAccessor; if( m_loadRequestMap.Find( requestConstAccessor, path ) ) { LoadRequest* pRequest = requestConstAccessor->Second(); HELIUM_ASSERT( pRequest ); AtomicIncrementRelease( pRequest->requestCount ); // We can release now, as the request shouldn't get released now that we've incremented its reference count. requestConstAccessor.Release(); return m_loadRequestPool.GetIndex( pRequest ); } // Get the package loader to use for the given object. PackageLoader* pPackageLoader = GetPackageLoader( path ); if( !pPackageLoader ) { HELIUM_TRACE( TRACE_ERROR, TXT( "GameObjectLoader::BeginLoadObject(): Failed to locate package loader for \"%s\".\n" ), *path.ToString() ); return Invalid< size_t >(); } // Add the load request. LoadRequest* pRequest = m_loadRequestPool.Allocate(); pRequest->path = path; HELIUM_ASSERT( !pRequest->spObject ); pRequest->pPackageLoader = pPackageLoader; SetInvalid( pRequest->packageLoadRequestId ); HELIUM_ASSERT( pRequest->linkTable.IsEmpty() ); pRequest->stateFlags = 0; pRequest->requestCount = 1; ConcurrentHashMap< GameObjectPath, LoadRequest* >::Accessor requestAccessor; if( m_loadRequestMap.Insert( requestAccessor, KeyValue< GameObjectPath, LoadRequest* >( path, pRequest ) ) ) { // New load request was created, so tick it once to get the load process running. requestAccessor.Release(); TickLoadRequest( pRequest ); } else { // A matching request was added while we were building our request, so reuse it. m_loadRequestPool.Release( pRequest ); pRequest = requestAccessor->Second(); HELIUM_ASSERT( pRequest ); AtomicIncrementRelease( pRequest->requestCount ); // We can release now, as the request shouldn't get released now that we've incremented its reference count. requestAccessor.Release(); } return m_loadRequestPool.GetIndex( pRequest ); }
/// 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. GameObject* GameObject::FindObject( GameObjectPath 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( GameObjectPath 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( GameObjectPath 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 ); }
/// Initialize this manager. /// /// @return True if this manager was initialized successfully, false if not. /// /// @see Shutdown() bool WorldManager::Initialize() { HELIUM_ASSERT( !m_spWorldPackage ); // 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. GameObjectPath worldPackagePath = GetWorldPackagePath(); HELIUM_ASSERT( !worldPackagePath.IsEmpty() ); HELIUM_ASSERT( worldPackagePath.GetParent().IsEmpty() ); bool bCreateResult = GameObject::Create< Package >( m_spWorldPackage, worldPackagePath.GetName(), NULL ); HELIUM_ASSERT( bCreateResult ); if( !bCreateResult ) { HELIUM_TRACE( TRACE_ERROR, TXT( "WorldManager::Initialize(): Failed to create world package \"%s\".\n" ), *worldPackagePath.ToString() ); return false; } HELIUM_ASSERT( m_spWorldPackage ); // 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; }
/// Resolve a dependency on an object reference. /// /// @param[in] path Object path. /// /// @return Dependency index. /// /// @see ResolveTypeDependency() uint32_t BinarySerializer::ResolveObjectDependency( GameObjectPath path ) { uint32_t objectIndex; SetInvalid( objectIndex ); if( !path.IsEmpty() ) { size_t dependencyCount = m_objectDependencies.GetSize(); for( size_t dependencyIndex = 0; dependencyIndex < dependencyCount; ++dependencyIndex ) { if( m_objectDependencies[ dependencyIndex ] == path ) { return static_cast< uint32_t >( dependencyIndex ); } } HELIUM_ASSERT( dependencyCount < UINT32_MAX ); m_objectDependencies.Push( path ); objectIndex = static_cast< uint32_t >( dependencyCount ); } return objectIndex; }
/// @copydoc ResourceHandler::CacheResource() bool ShaderResourceHandler::CacheResource( ObjectPreprocessor* pObjectPreprocessor, Resource* pResource, const String& rSourceFilePath ) { HELIUM_ASSERT( pObjectPreprocessor ); HELIUM_ASSERT( pResource ); const Shader* pShader = Reflect::AssertCast< const Shader >( pResource ); GameObjectPath shaderPath = pShader->GetPath(); HELIUM_TRACE( TraceLevels::Info, TXT( "ShaderResourceHandler: Caching \"%s\".\n" ), *shaderPath.ToString() ); DefaultAllocator allocator; FileStream* pSourceFileStream = FileStream::OpenFileStream( rSourceFilePath, FileStream::MODE_READ ); if( !pSourceFileStream ) { HELIUM_TRACE( TraceLevels::Error, TXT( "ShaderResourceHandler: Source file for shader resource \"%s\" failed to open properly.\n" ), *shaderPath.ToString() ); return false; } // Load the entire shader resource into memory. int64_t size64 = pSourceFileStream->GetSize(); HELIUM_ASSERT( size64 != -1 ); HELIUM_ASSERT( static_cast< uint64_t >( size64 ) <= static_cast< size_t >( -1 ) ); if( size64 > static_cast< uint64_t >( static_cast< size_t >( -1 ) ) ) { HELIUM_TRACE( TraceLevels::Error, ( TXT( "ShaderResourceHandler: Source file for shader resource \"%s\" is too large to fit into " ) TXT( "memory for preprocessing.\n" ) ), *shaderPath.ToString() ); delete pSourceFileStream; return false; } size_t size = static_cast< size_t >( size64 ); void* pShaderData = allocator.Allocate( size ); HELIUM_ASSERT( pShaderData ); if( !pShaderData ) { HELIUM_TRACE( TraceLevels::Error, ( TXT( "ShaderResourceHandler: Failed to allocate %" ) TPRIuSZ TXT( " bytes for loading the source " ) TXT( "data of \"%s\" for preprocessing.\n" ) ), size, *shaderPath.ToString() ); delete pSourceFileStream; return false; } BufferedStream( pSourceFileStream ).Read( pShaderData, 1, size ); delete pSourceFileStream; // Parse all preprocessor toggle and selection tokens from the shader source. Shader::PersistentResourceData resourceData; const char* pLineEnd = static_cast< const char* >( pShaderData ); const char* pShaderEnd = pLineEnd + size; do { const char* pLineStart = pLineEnd; while( pLineEnd < pShaderEnd ) { char character = *pLineEnd; if( character == '\n' || character == '\r' ) { break; } ++pLineEnd; } ParseLine( shaderPath, resourceData, pLineStart, pLineEnd ); while( pLineEnd < pShaderEnd ) { char character = *pLineEnd; if( character != '\n' && character != '\r' ) { break; } ++pLineEnd; } } while( pLineEnd < pShaderEnd ); allocator.Free( pShaderData ); // Serialize the persistent shader resource data for each platform. for( size_t platformIndex = 0; platformIndex < static_cast< size_t >( Cache::PLATFORM_MAX ); ++platformIndex ) { PlatformPreprocessor* pPreprocessor = pObjectPreprocessor->GetPlatformPreprocessor( static_cast< Cache::EPlatform >( platformIndex ) ); if( !pPreprocessor ) { continue; } Resource::PreprocessedData& rPreprocessedData = pResource->GetPreprocessedData( static_cast< Cache::EPlatform >( platformIndex ) ); SaveObjectToPersistentDataBuffer(&resourceData, rPreprocessedData.persistentDataBuffer); rPreprocessedData.subDataBuffers.Resize( 0 ); rPreprocessedData.bLoaded = true; } return true; }
/// Parse the given shader source line for toggle and select options. /// /// @param[in] shaderPath GameObject path of the shader resource being preprocessed (used for logging purposes /// only). /// @param[in] rResourceData Persistent shader resource data to update. /// @param[in] pLineStart Pointer to the first character in the line. /// @param[in] pLineEnd Pointer to the character just past the end of the line. void ShaderResourceHandler::ParseLine( GameObjectPath shaderPath, Shader::PersistentResourceData& rResourceData, const char* pLineStart, const char* pLineEnd ) { HELIUM_UNREF( shaderPath ); // Not used if logging is disabled. HELIUM_ASSERT( pLineStart ); HELIUM_ASSERT( pLineEnd >= pLineStart ); const char linePrefix[] = "//!"; const char toggleUserCommand[] = "@toggle"; const char selectUserCommand[] = "@select"; const char toggleSystemCommand[] = "@systoggle"; const char selectSystemCommand[] = "@sysselect"; size_t characterCount = static_cast< size_t >( pLineEnd - pLineStart ); // Only process lines that start with the special comment prefix. if( characterCount < HELIUM_ARRAY_COUNT( linePrefix ) - 1 || CompareString( pLineStart, linePrefix, HELIUM_ARRAY_COUNT( linePrefix ) - 1 ) != 0 ) { return; } pLineStart += HELIUM_ARRAY_COUNT( linePrefix ) - 1; characterCount -= HELIUM_ARRAY_COUNT( linePrefix ) - 1; // Split the line based on groups of whitespaces. CharString line( pLineStart, characterCount ); DynamicArray< CharString > splitLine; line.Split( splitLine, " \t\v\f", Invalid< size_t >(), true ); // Ignore the first split if it's empty (will occur if the command is preceded by whitespaces). size_t splitCount = splitLine.GetSize(); if( splitCount > 0 && splitLine[ 0 ].IsEmpty() ) { splitLine.Remove( 0 ); --splitCount; } // We need at least 2 splits (command and at least one command parameter). if( splitCount < 2 ) { return; } // Process the command. DynamicArray< CharString > splitCommand; splitLine[ 0 ].Split( splitCommand, '_' ); size_t commandSplitCount = splitCommand.GetSize(); if( commandSplitCount < 1 || commandSplitCount > 2 ) { // Invalid command format. return; } const CharString& rCommand = splitCommand[ 0 ]; bool bToggleUserCommand = ( rCommand == toggleUserCommand ); bool bSelectUserCommand = ( !bToggleUserCommand && rCommand == selectUserCommand ); bool bToggleSystemCommand = ( !( bToggleUserCommand | bSelectUserCommand ) && rCommand == toggleSystemCommand ); bool bSelectSystemCommand = ( !( bToggleUserCommand | bSelectUserCommand | bToggleSystemCommand ) && rCommand == selectSystemCommand ); if( !( bToggleUserCommand | bSelectUserCommand | bToggleSystemCommand | bSelectSystemCommand ) ) { return; } /// Make sure the option name (first parameter after the command name) is valid. String convertedString; HELIUM_VERIFY( ( StringConverter< char, tchar_t >::Convert( convertedString, splitLine[ 1 ] ) ) ); Name optionName( convertedString ); if( optionName.IsEmpty() ) { HELIUM_TRACE( TraceLevels::Error, TXT( "ShaderResourceHandler: Skipping empty option in shader resource \"%s\".\n" ), *shaderPath.ToString() ); return; } // Make sure an existing toggle or selection option exists with the parsed option name. Shader::Options& rSystemOptions = rResourceData.GetSystemOptions(); Shader::Options& rUserOptions = rResourceData.GetUserOptions(); DynamicArray< Shader::Toggle >& rSystemToggles = rSystemOptions.GetToggles(); DynamicArray< Shader::Select >& rSystemSelects = rSystemOptions.GetSelects(); DynamicArray< Shader::Toggle >& rUserToggles = rUserOptions.GetToggles(); DynamicArray< Shader::Select >& rUserSelects = rUserOptions.GetSelects(); if( ParseLineDuplicateOptionCheck( optionName, rSystemToggles ) || ParseLineDuplicateOptionCheck( optionName, rSystemSelects ) || ParseLineDuplicateOptionCheck( optionName, rUserToggles ) || ParseLineDuplicateOptionCheck( optionName, rUserSelects ) ) { HELIUM_TRACE( TraceLevels::Error, ( TXT( "ShaderResourceHandler: Duplicate option name \"%s\" found in shader resource \"%s\". Only " ) TXT( "the first option will be used.\n" ) ), *optionName, *shaderPath.ToString() ); return; } // Handle shader-specific command flags (option applies to all shader types if no flags are specified). uint32_t shaderFlags = ( 1 << RShader::TYPE_MAX ) - 1; if( commandSplitCount > 1 ) { shaderFlags = 0; const CharString& rShaderFlags = splitCommand[ 1 ]; size_t shaderFlagCount = rShaderFlags.GetSize(); for( size_t flagIndex = 0; flagIndex < shaderFlagCount; ++flagIndex ) { char flagCharacter = rShaderFlags[ flagIndex ]; if( flagCharacter == 'v' ) { shaderFlags |= ( 1 << RShader::TYPE_VERTEX ); } else if( flagCharacter == 'p' ) { shaderFlags |= ( 1 << RShader::TYPE_PIXEL ); } } } // Parse the command parameters. if( bToggleUserCommand | bToggleSystemCommand ) { Shader::Toggle* pToggle = ( bToggleUserCommand ? rUserToggles : rSystemToggles ).New(); HELIUM_ASSERT( pToggle ); pToggle->name = optionName; pToggle->shaderTypeFlags = shaderFlags; if( splitCount > 2 ) { HELIUM_TRACE( TraceLevels::Warning, ( TXT( "ShaderResourceHandler: Extra tokens for toggle command \"%s\" in shader resource \"%s\" " ) TXT( "ignored.\n" ) ), *splitLine[ 1 ], *shaderPath.ToString() ); } } else { if( splitCount < 3 || ( splitCount < 4 && splitLine[ 2 ] == "NONE" ) ) { HELIUM_TRACE( TraceLevels::Error, ( TXT( "ShaderResourceHandler: Missing options for select command \"%s\" in shader resource " ) TXT( "\"%s\".\n" ) ), *splitLine[ 1 ], *shaderPath.ToString() ); return; } Shader::Select* pSelect = ( bSelectUserCommand ? rUserSelects : rSystemSelects ).New(); HELIUM_ASSERT( pSelect ); pSelect->name = optionName; pSelect->shaderTypeFlags = shaderFlags; pSelect->bOptional = ( splitLine[ 2 ] == "NONE" ); size_t choiceIndex = ( pSelect->bOptional ? 3 : 2 ); for( ; choiceIndex < splitCount; ++choiceIndex ) { HELIUM_VERIFY( ( StringConverter< char, tchar_t >::Convert( convertedString, splitLine[ choiceIndex ] ) ) ); pSelect->choices.New( convertedString ); } } }
/// Update property preloading for the given object load request. /// /// @param[in] pRequest Load request to update. /// /// @return True if preloading still needs processing, false if it is complete. bool GameObjectLoader::TickPreload( LoadRequest* pRequest ) { HELIUM_ASSERT( pRequest ); HELIUM_ASSERT( !( pRequest->stateFlags & ( LOAD_FLAG_LINKED | LOAD_FLAG_PRECACHED | LOAD_FLAG_LOADED ) ) ); PackageLoader* pPackageLoader = pRequest->pPackageLoader; HELIUM_ASSERT( pPackageLoader ); if( IsInvalid( pRequest->packageLoadRequestId ) ) { if( !pPackageLoader->TryFinishPreload() ) { // Still waiting for package loader preload. return false; } // Add an object load request. GameObjectPath path = pRequest->path; pRequest->packageLoadRequestId = pPackageLoader->BeginLoadObject( path ); if( IsInvalid( pRequest->packageLoadRequestId ) ) { pRequest->spObject = GameObject::FindObject( path ); GameObject* pObject = pRequest->spObject; if( pObject ) { HELIUM_TRACE( TRACE_WARNING, TXT( "GameObjectLoader: GameObject \"%s\" is not serialized, but was found in memory.\n" ), *path.ToString() ); // Make sure the object is preloaded and linked, but still perform resource caching and load // finalization if necessary. pObject->SetFlags( GameObject::FLAG_PRELOADED | GameObject::FLAG_LINKED ); AtomicOrRelease( pRequest->stateFlags, LOAD_FLAG_PRELOADED | LOAD_FLAG_LINKED ); return true; } HELIUM_TRACE( TRACE_ERROR, TXT( "GameObjectLoader: GameObject \"%s\" is not serialized and does not exist in memory.\n" ), *path.ToString() ); AtomicOrRelease( pRequest->stateFlags, LOAD_FLAG_FULLY_LOADED | LOAD_FLAG_ERROR ); return true; } } HELIUM_ASSERT( IsValid( pRequest->packageLoadRequestId ) ); bool bFinished = pPackageLoader->TryFinishLoadObject( pRequest->packageLoadRequestId, pRequest->spObject, pRequest->linkTable ); if( !bFinished ) { // Still waiting for object to load. return false; } // Preload complete. SetInvalid( pRequest->packageLoadRequestId ); AtomicOrRelease( pRequest->stateFlags, LOAD_FLAG_PRELOADED ); return true; }
/// Deserialize the link tables for an object load. /// /// @param[in] pRequest Load request data. bool CachePackageLoader::DeserializeLinkTables( LoadRequest* pRequest ) { HELIUM_ASSERT( pRequest ); uint8_t* pBufferCurrent = pRequest->pAsyncLoadBuffer; uint8_t* pPropertyStreamEnd = pRequest->pPropertyStreamEnd; HELIUM_ASSERT( pBufferCurrent ); HELIUM_ASSERT( pPropertyStreamEnd ); HELIUM_ASSERT( pBufferCurrent <= pPropertyStreamEnd ); uint32_t propertyStreamSize = 0; if( pBufferCurrent + sizeof( propertyStreamSize ) > pPropertyStreamEnd ) { HELIUM_TRACE( TraceLevels::Error, TXT( "CachePackageLoader: End of buffer reached when attempting to deserialize \"%s\".\n" ), *pRequest->pEntry->path.ToString() ); return false; } MemoryCopy( &propertyStreamSize, pBufferCurrent, sizeof( propertyStreamSize ) ); pBufferCurrent += sizeof( propertyStreamSize ); if( propertyStreamSize > static_cast< size_t >( pPropertyStreamEnd - pBufferCurrent ) ) { HELIUM_TRACE( TraceLevels::Error, ( TXT( "CachePackageLoader: Property stream size (%" ) TPRIu32 TXT( " bytes) for \"%s\" exceeds the " ) TXT( "amount of data cached. Value will be clamped.\n" ) ), propertyStreamSize, *pRequest->pEntry->path.ToString() ); propertyStreamSize = static_cast< uint32_t >( pPropertyStreamEnd - pBufferCurrent ); } pPropertyStreamEnd = pBufferCurrent + propertyStreamSize; pRequest->pPropertyStreamEnd = pPropertyStreamEnd; // Adjust the end of the persistent resource data stream to account for the resource sub-data count padded on // the end (note that non-resources will not have this padding). if( pRequest->pPersistentResourceStreamEnd - pPropertyStreamEnd >= sizeof( uint32_t ) ) { pRequest->pPersistentResourceStreamEnd -= sizeof( uint32_t ); } else { pRequest->pPersistentResourceStreamEnd = pPropertyStreamEnd; } StackMemoryHeap<>& rStackHeap = ThreadLocalStackAllocator::GetMemoryHeap(); // Load the type link table. uint32_t typeLinkTableSize = 0; if( pBufferCurrent + sizeof( typeLinkTableSize ) > pPropertyStreamEnd ) { HELIUM_TRACE( TraceLevels::Error, TXT( "CachePackageLoader: End of buffer reached when attempting to deserialize \"%s\".\n" ), *pRequest->pEntry->path.ToString() ); return false; } MemoryCopy( &typeLinkTableSize, pBufferCurrent, sizeof( typeLinkTableSize ) ); pBufferCurrent += sizeof( typeLinkTableSize ); pRequest->typeLinkTable.Resize( 0 ); pRequest->typeLinkTable.Reserve( typeLinkTableSize ); uint_fast32_t typeLinkTableSizeFast = typeLinkTableSize; for( uint_fast32_t linkTableIndex = 0; linkTableIndex < typeLinkTableSizeFast; ++linkTableIndex ) { uint32_t typeNameSize; if( pBufferCurrent + sizeof( typeNameSize ) > pPropertyStreamEnd ) { HELIUM_TRACE( TraceLevels::Error, TXT( "CachePackageLoader: End of buffer reached when attempting to deserialize \"%s\".\n" ), *pRequest->pEntry->path.ToString() ); return false; } MemoryCopy( &typeNameSize, pBufferCurrent, sizeof( typeNameSize ) ); pBufferCurrent += sizeof( typeNameSize ); if( pBufferCurrent + sizeof( tchar_t ) * typeNameSize > pPropertyStreamEnd ) { HELIUM_TRACE( TraceLevels::Error, TXT( "CachePackageLoader: End of buffer reached when attempting to deserialize \"%s\".\n" ), *pRequest->pEntry->path.ToString() ); return false; } StackMemoryHeap<>::Marker stackMarker( rStackHeap ); tchar_t* pTypeNameString = static_cast< tchar_t* >( rStackHeap.Allocate( sizeof( tchar_t ) * ( typeNameSize + 1 ) ) ); HELIUM_ASSERT( pTypeNameString ); MemoryCopy( pTypeNameString, pBufferCurrent, sizeof( tchar_t ) * typeNameSize ); pBufferCurrent += sizeof( tchar_t ) * typeNameSize; pTypeNameString[ typeNameSize ] = TXT( '\0' ); Name typeName( pTypeNameString ); GameObjectType* pType = GameObjectType::Find( typeName ); if( !pType ) { HELIUM_TRACE( TraceLevels::Error, TXT( "CachePackageLoader: Failed to locate type \"%s\" when attempting to deserialize \"%s\".\n" ), pTypeNameString, *pRequest->pEntry->path.ToString() ); } pRequest->typeLinkTable.Push( pType ); } // Load the object link table. uint32_t objectLinkTableSize = 0; if( pBufferCurrent + sizeof( objectLinkTableSize ) > pPropertyStreamEnd ) { HELIUM_TRACE( TraceLevels::Error, TXT( "CachePackageLoader: End of buffer reached when attempting to deserialize \"%s\".\n" ), *pRequest->pEntry->path.ToString() ); return false; } MemoryCopy( &objectLinkTableSize, pBufferCurrent, sizeof( objectLinkTableSize ) ); pBufferCurrent += sizeof( objectLinkTableSize ); pRequest->objectLinkTable.Resize( 0 ); pRequest->objectLinkTable.Reserve( objectLinkTableSize ); StackMemoryHeap<>::Marker stackMarker( rStackHeap ); // Track the link table object paths so that we can use them for issuing additional load requests for the object // template and owner dependencies (this way, we can sync on those load requests during the preload process // while still providing load requests for the caller to resolve if necessary). GameObjectPath* pObjectLinkTablePaths = static_cast< GameObjectPath* >( rStackHeap.Allocate( sizeof( GameObjectPath ) * objectLinkTableSize ) ); HELIUM_ASSERT( pObjectLinkTablePaths ); ArrayUninitializedFill( pObjectLinkTablePaths, GameObjectPath( NULL_NAME ), objectLinkTableSize ); GameObjectLoader* pObjectLoader = GameObjectLoader::GetStaticInstance(); HELIUM_ASSERT( pObjectLoader ); uint_fast32_t objectLinkTableSizeFast = objectLinkTableSize; for( uint_fast32_t linkTableIndex = 0; linkTableIndex < objectLinkTableSizeFast; ++linkTableIndex ) { uint32_t pathStringSize; if( pBufferCurrent + sizeof( pathStringSize ) > pPropertyStreamEnd ) { HELIUM_TRACE( TraceLevels::Error, TXT( "CachePackageLoader: End of buffer reached when attempting to deserialize \"%s\".\n" ), *pRequest->pEntry->path.ToString() ); return false; } MemoryCopy( &pathStringSize, pBufferCurrent, sizeof( pathStringSize ) ); pBufferCurrent += sizeof( pathStringSize ); if( pBufferCurrent + sizeof( tchar_t ) * pathStringSize > pPropertyStreamEnd ) { HELIUM_TRACE( TraceLevels::Error, TXT( "CachePackageLoader: End of buffer reached when attempting to deserialize \"%s\".\n" ), *pRequest->pEntry->path.ToString() ); return false; } StackMemoryHeap<>::Marker stackMarker( rStackHeap ); tchar_t* pPathString = static_cast< tchar_t* >( rStackHeap.Allocate( sizeof( tchar_t ) * ( pathStringSize + 1 ) ) ); HELIUM_ASSERT( pPathString ); MemoryCopy( pPathString, pBufferCurrent, sizeof( tchar_t ) * pathStringSize ); pBufferCurrent += sizeof( tchar_t ) * pathStringSize; pPathString[ pathStringSize ] = TXT( '\0' ); size_t linkLoadId; SetInvalid( linkLoadId ); GameObjectPath path; if( !path.Set( pPathString ) ) { HELIUM_TRACE( TraceLevels::Error, ( TXT( "CachePackageLoader: Invalid object path \"%s\" found in linker table when deserializing " ) TXT( "\"%s\". Setting to null.\n" ) ), pPathString, *pRequest->pEntry->path.ToString() ); pRequest->flags |= LOAD_FLAG_ERROR; } else { pObjectLinkTablePaths[ linkTableIndex ] = path; // Begin loading the link table entry. linkLoadId = pObjectLoader->BeginLoadObject( path ); if( IsInvalid( linkLoadId ) ) { HELIUM_TRACE( TraceLevels::Error, ( TXT( "CachePackageLoader: Failed to begin loading \"%s\" as a link dependency for \"%s\". " ) TXT( "Setting to null.\n" ) ), pPathString, *pRequest->pEntry->path.ToString() ); pRequest->flags |= LOAD_FLAG_ERROR; } } pRequest->objectLinkTable.Push( linkLoadId ); } // Read the type link information. uint32_t typeLinkIndex; if( pBufferCurrent + sizeof( typeLinkIndex ) > pPropertyStreamEnd ) { HELIUM_TRACE( TraceLevels::Error, TXT( "CachePackageLoader: End of buffer reached when attempting to deserialize \"%s\".\n" ), *pRequest->pEntry->path.ToString() ); return false; } MemoryCopy( &typeLinkIndex, pBufferCurrent, sizeof( typeLinkIndex ) ); pBufferCurrent += sizeof( typeLinkIndex ); if( typeLinkIndex >= typeLinkTableSizeFast ) { HELIUM_TRACE( TraceLevels::Error, TXT( "CachePackageLoader: Invalid link table index for the type of \"%s\".\n" ), *pRequest->pEntry->path.ToString() ); return false; } GameObjectType* pType = pRequest->typeLinkTable[ typeLinkIndex ]; if( !pType ) { HELIUM_TRACE( TraceLevels::Error, TXT( "CachePackageLoader: Type not found for object \"%s\".\n" ), *pRequest->pEntry->path.ToString() ); return false; } pRequest->spType = pType; // Read the template link information. if( pBufferCurrent + sizeof( pRequest->templateLinkIndex ) > pPropertyStreamEnd ) { HELIUM_TRACE( TraceLevels::Error, TXT( "CachePackageLoader: End of buffer reached when attempting to deserialize \"%s\".\n" ), *pRequest->pEntry->path.ToString() ); return false; } MemoryCopy( &pRequest->templateLinkIndex, pBufferCurrent, sizeof( pRequest->templateLinkIndex ) ); pBufferCurrent += sizeof( pRequest->templateLinkIndex ); if( IsValid( pRequest->templateLinkIndex ) ) { if( pRequest->templateLinkIndex >= objectLinkTableSizeFast ) { HELIUM_TRACE( TraceLevels::Error, TXT( "CachePackageLoader: Invalid link table index for the template of \"%s\".\n" ), *pRequest->pEntry->path.ToString() ); SetInvalid( pRequest->templateLinkIndex ); return false; } size_t templateLoadId = pObjectLoader->BeginLoadObject( pObjectLinkTablePaths[ pRequest->templateLinkIndex ] ); HELIUM_ASSERT( templateLoadId == pRequest->objectLinkTable[ pRequest->templateLinkIndex ] ); HELIUM_UNREF( templateLoadId ); } // Read the owner link information. if( pBufferCurrent + sizeof( pRequest->ownerLinkIndex ) > pPropertyStreamEnd ) { HELIUM_TRACE( TraceLevels::Error, TXT( "CachePackageLoader: End of buffer reached when attempting to deserialize \"%s\".\n" ), *pRequest->pEntry->path.ToString() ); return false; } MemoryCopy( &pRequest->ownerLinkIndex, pBufferCurrent, sizeof( pRequest->ownerLinkIndex ) ); pBufferCurrent += sizeof( pRequest->ownerLinkIndex ); if( IsValid( pRequest->ownerLinkIndex ) ) { if( pRequest->ownerLinkIndex >= objectLinkTableSizeFast ) { HELIUM_TRACE( TraceLevels::Error, TXT( "CachePackageLoader: Invalid link table index for the owner of \"%s\".\n" ), *pRequest->pEntry->path.ToString() ); SetInvalid( pRequest->ownerLinkIndex ); return false; } size_t ownerLoadId = pObjectLoader->BeginLoadObject( pObjectLinkTablePaths[ pRequest->ownerLinkIndex ] ); HELIUM_ASSERT( ownerLoadId == pRequest->objectLinkTable[ pRequest->ownerLinkIndex ] ); HELIUM_UNREF( ownerLoadId ); } pRequest->pSerializedData = pBufferCurrent; return true; }
/// @copydoc PackageLoader::BeginLoadObject() size_t CachePackageLoader::BeginLoadObject( GameObjectPath path ) { HELIUM_ASSERT( m_pCache ); // Don't load packages from the cache, but instead create them dynamically. if( path.IsPackage() ) { HELIUM_TRACE( TraceLevels::Debug, TXT( "CachePackageLoader::BeginLoadObject(): \"%s\" is a package, resolving immediately.\n" ), *path.ToString() ); LoadRequest* pRequest = m_loadRequestPool.Allocate(); HELIUM_ASSERT( pRequest ); pRequest->pEntry = NULL; ResolvePackage( pRequest->spObject, path ); HELIUM_ASSERT( pRequest->spObject ); SetInvalid( pRequest->asyncLoadId ); pRequest->pAsyncLoadBuffer = NULL; pRequest->pSerializedData = NULL; pRequest->pPropertyStreamEnd = NULL; pRequest->pPersistentResourceStreamEnd = NULL; HELIUM_ASSERT( pRequest->typeLinkTable.IsEmpty() ); HELIUM_ASSERT( pRequest->objectLinkTable.IsEmpty() ); HELIUM_ASSERT( !pRequest->spType ); HELIUM_ASSERT( !pRequest->spTemplate ); HELIUM_ASSERT( !pRequest->spOwner ); SetInvalid( pRequest->templateLinkIndex ); SetInvalid( pRequest->ownerLinkIndex ); pRequest->flags = LOAD_FLAG_PRELOADED; size_t requestId = m_loadRequests.Add( pRequest ); return requestId; } const Cache::Entry* pEntry = m_pCache->FindEntry( path, 0 ); if( !pEntry ) { HELIUM_TRACE( TraceLevels::Debug, ( TXT( "CachePackageLoader::BeginLoadObject(): \"%s\" is not cached in this package. No load " ) TXT( "request added.\n" ) ), *path.ToString() ); 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->pEntry != pEntry ); if( pRequest->pEntry == pEntry ) { HELIUM_TRACE( TraceLevels::Error, ( TXT( "CachePackageLoader::BeginLoadObject(): Duplicate load request of \"%s\". No load " ) TXT( "request added.\n" ) ), *path.ToString() ); return Invalid< size_t >(); } } #endif LoadRequest* pRequest = m_loadRequestPool.Allocate(); HELIUM_ASSERT( pRequest ); pRequest->pEntry = pEntry; HELIUM_ASSERT( !pRequest->spObject ); SetInvalid( pRequest->asyncLoadId ); pRequest->pAsyncLoadBuffer = NULL; pRequest->pSerializedData = NULL; pRequest->pPropertyStreamEnd = NULL; pRequest->pPersistentResourceStreamEnd = NULL; HELIUM_ASSERT( pRequest->typeLinkTable.IsEmpty() ); HELIUM_ASSERT( pRequest->objectLinkTable.IsEmpty() ); HELIUM_ASSERT( !pRequest->spType ); HELIUM_ASSERT( !pRequest->spTemplate ); HELIUM_ASSERT( !pRequest->spOwner ); SetInvalid( pRequest->templateLinkIndex ); SetInvalid( pRequest->ownerLinkIndex ); 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). pRequest->spObject = GameObject::FindObject( pEntry->path ); GameObject* pObject = pRequest->spObject; if( pObject && pObject->IsFullyLoaded() ) { HELIUM_TRACE( TraceLevels::Debug, ( TXT( "CachePackageLoader::BeginLoadObject(): \"%s\" is already fully loaded. Bypassing load " ) TXT( "process.\n" ) ), *path.ToString() ); pRequest->flags = LOAD_FLAG_PRELOADED; } else { HELIUM_ASSERT( !pObject || !pObject->GetAnyFlagSet( GameObject::FLAG_LOADED | GameObject::FLAG_LINKED ) ); HELIUM_TRACE( TraceLevels::Debug, TXT( "CachePackageLoader::BeginLoadObject(): Issuing async load of property data for \"%s\".\n" ), *path.ToString() ); size_t entrySize = pEntry->size; pRequest->pAsyncLoadBuffer = static_cast< uint8_t* >( DefaultAllocator().Allocate( entrySize ) ); HELIUM_ASSERT( pRequest->pAsyncLoadBuffer ); AsyncLoader& rLoader = AsyncLoader::GetStaticInstance(); pRequest->asyncLoadId = rLoader.QueueRequest( pRequest->pAsyncLoadBuffer, m_pCache->GetCacheFileName(), pEntry->offset, entrySize ); HELIUM_ASSERT( IsValid( pRequest->asyncLoadId ) ); } size_t requestId = m_loadRequests.Add( pRequest ); HELIUM_TRACE( TraceLevels::Debug, ( TXT( "CachePackageLoader::BeginLoadObject(): Load request for \"%s\" added (ID: %" ) TPRIuSZ TXT( ").\n" ) ), *path.ToString(), requestId ); return requestId; }
/// Finalize the TOC loading process. /// /// Note that this does not free any resources on a failed load (the caller is responsible for such clean-up work). /// /// @return True if the TOC load was successful, false if not. bool Cache::FinalizeTocLoad() { HELIUM_ASSERT( m_pTocBuffer ); const uint8_t* pTocCurrent = m_pTocBuffer; const uint8_t* pTocMax = pTocCurrent + m_tocSize; StackMemoryHeap<>& rStackHeap = ThreadLocalStackAllocator::GetMemoryHeap(); // Validate the TOC header. uint32_t magic; if( !CheckedTocRead( MemoryCopy, magic, TXT( "the header magic" ), pTocCurrent, pTocMax ) ) { return false; } LOAD_VALUE_CALLBACK* pLoadFunction = NULL; if( magic == TOC_MAGIC ) { HELIUM_TRACE( TraceLevels::Info, TXT( "Cache::FinalizeTocLoad(): TOC \"%s\" identified (no byte swapping).\n" ), *m_tocFileName ); pLoadFunction = MemoryCopy; } else if( magic == TOC_MAGIC_SWAPPED ) { HELIUM_TRACE( TraceLevels::Info, TXT( "Cache::FinalizeTocLoad(): TOC \"%s\" identified (byte swapping is necessary).\n" ), *m_tocFileName ); HELIUM_TRACE( TraceLevels::Warning, ( TXT( "Cache::TryFinishLoadToc(): Cache for TOC \"%s\" uses byte swapping, which may incur " ) TXT( "performance penalties.\n" ) ), *m_tocFileName ); pLoadFunction = ReverseByteOrder; } else { HELIUM_TRACE( TraceLevels::Error, TXT( "Cache::FinalizeTocLoad(): TOC \"%s\" has invalid file magic.\n" ), *m_tocFileName ); return false; } uint32_t version; if( !CheckedTocRead( pLoadFunction, version, TXT( "the cache version number" ), pTocCurrent, pTocMax ) ) { return false; } if( version > sm_Version ) { HELIUM_TRACE( TraceLevels::Error, ( TXT( "Cache::FinalizeTocLoad(): Cache version number (%" ) TPRIu32 TXT( ") exceeds the maximum " ) TXT( "supported version (%" ) TPRIu32 TXT( ").\n" ) ), version, sm_Version ); return false; } // Read the numbers of entries in the cache. uint32_t entryCount; bool bReadResult = CheckedTocRead( pLoadFunction, entryCount, TXT( "the number of entries in the cache" ), pTocCurrent, pTocMax ); if( !bReadResult ) { return false; } // Load the entry information. EntryKey key; uint_fast32_t entryCountFast = entryCount; m_entries.Reserve( entryCountFast ); for( uint_fast32_t entryIndex = 0; entryIndex < entryCountFast; ++entryIndex ) { uint16_t entryPathSize; bReadResult = CheckedTocRead( pLoadFunction, entryPathSize, TXT( "entry GameObjectPath string size" ), pTocCurrent, pTocMax ); if( !bReadResult ) { return false; } uint_fast16_t entryPathSizeFast = entryPathSize; StackMemoryHeap<>::Marker stackMarker( rStackHeap ); tchar_t* pPathString = static_cast< tchar_t* >( rStackHeap.Allocate( sizeof( tchar_t ) * ( entryPathSizeFast + 1 ) ) ); HELIUM_ASSERT( pPathString ); pPathString[ entryPathSizeFast ] = TXT( '\0' ); for( uint_fast16_t characterIndex = 0; characterIndex < entryPathSizeFast; ++characterIndex ) { #if HELIUM_WCHAR_T bReadResult = CheckedTocRead( pLoadFunction, pPathString[ characterIndex ], TXT( "entry GameObjectPath string character" ), pTocCurrent, pTocMax ); if( !bReadResult ) { return false; } #else wchar_t character; bReadResult = CheckedTocRead( pLoadFunction, character, TXT( "entry GameObjectPath string character" ), pTocCurrent, pTocMax ); if( !bReadResult ) { return false; } // TODO: Implement locale-specific conversion from Unicode to MBCS. pPathString[ characterIndex ] = ( character >= 0x80 ? '?' : static_cast< char >( character ) ); #endif } GameObjectPath entryPath; if( !entryPath.Set( pPathString ) ) { HELIUM_TRACE( TraceLevels::Error, TXT( "Cache::FinalizeTocLoad(): Failed to set GameObjectPath for entry %" ) TPRIuFAST16 TXT( ".\n" ), entryIndex ); return false; } uint32_t entrySubDataIndex; bReadResult = CheckedTocRead( pLoadFunction, entrySubDataIndex, TXT( "entry sub-data index" ), pTocCurrent, pTocMax ); if( !bReadResult ) { return false; } key.path = entryPath; key.subDataIndex = entrySubDataIndex; EntryMapType::ConstAccessor entryAccessor; if( m_entryMap.Find( entryAccessor, key ) ) { HELIUM_TRACE( TraceLevels::Error, ( TXT( "Cache::FinalizeTocLoad(): Duplicate entry found for GameObjectPath \"%s\", sub-data %" ) TPRIu32 TXT( ".\n" ) ), pPathString, entrySubDataIndex ); return false; } uint64_t entryOffset; if( !CheckedTocRead( pLoadFunction, entryOffset, TXT( "entry offset" ), pTocCurrent, pTocMax ) ) { return false; } int64_t entryTimestamp; if( !CheckedTocRead( pLoadFunction, entryTimestamp, TXT( "entry timestamp" ), pTocCurrent, pTocMax ) ) { return false; } uint32_t entrySize; if( !CheckedTocRead( pLoadFunction, entrySize, TXT( "entry size" ), pTocCurrent, pTocMax ) ) { return false; } Entry* pEntry = m_pEntryPool->Allocate(); HELIUM_ASSERT( pEntry ); pEntry->path = entryPath; pEntry->subDataIndex = entrySubDataIndex; pEntry->offset = entryOffset; pEntry->timestamp = entryTimestamp; pEntry->size = entrySize; m_entries.Add( pEntry ); HELIUM_VERIFY( m_entryMap.Insert( entryAccessor, KeyValue< EntryKey, Entry* >( key, pEntry ) ) ); } return true; }
/// Add or update an entry in the cache. /// /// @param[in] path GameObject path. /// @param[in] subDataIndex Sub-data index associated with the cached data. /// @param[in] pData Data to cache. /// @param[in] timestamp Timestamp value to associate with the entry in the cache. /// @param[in] size Number of bytes to cache. /// /// @return True if the cache was updated successfully, false if not. bool Cache::CacheEntry( GameObjectPath path, uint32_t subDataIndex, const void* pData, int64_t timestamp, uint32_t size ) { HELIUM_ASSERT( pData || size == 0 ); Status status; status.Read( m_cacheFileName.GetData() ); int64_t cacheFileSize = status.m_Size; uint64_t entryOffset = ( cacheFileSize == -1 ? 0 : static_cast< uint64_t >( cacheFileSize ) ); HELIUM_ASSERT( m_pEntryPool ); Entry* pEntryUpdate = m_pEntryPool->Allocate(); HELIUM_ASSERT( pEntryUpdate ); pEntryUpdate->offset = entryOffset; pEntryUpdate->timestamp = timestamp; pEntryUpdate->path = path; pEntryUpdate->subDataIndex = subDataIndex; pEntryUpdate->size = size; uint64_t originalOffset = 0; int64_t originalTimestamp = 0; uint32_t originalSize = 0; EntryKey key; key.path = path; key.subDataIndex = subDataIndex; EntryMapType::Accessor entryAccessor; bool bNewEntry = m_entryMap.Insert( entryAccessor, KeyValue< EntryKey, Entry* >( key, pEntryUpdate ) ); if( bNewEntry ) { HELIUM_TRACE( TraceLevels::Info, TXT( "Cache: Adding \"%s\" to cache \"%s\".\n" ), *path.ToString(), *m_cacheFileName ); m_entries.Push( pEntryUpdate ); } else { HELIUM_TRACE( TraceLevels::Info, TXT( "Cache: Updating \"%s\" in cache \"%s\".\n" ), *path.ToString(), *m_cacheFileName ); m_pEntryPool->Release( pEntryUpdate ); pEntryUpdate = entryAccessor->Second(); HELIUM_ASSERT( pEntryUpdate ); originalOffset = pEntryUpdate->offset; originalTimestamp = pEntryUpdate->timestamp; originalSize = pEntryUpdate->size; if( originalSize < size ) { pEntryUpdate->offset = entryOffset; } else { entryOffset = originalOffset; } pEntryUpdate->timestamp = timestamp; pEntryUpdate->size = size; } AsyncLoader& rLoader = AsyncLoader::GetStaticInstance(); rLoader.Lock(); bool bCacheSuccess = true; FileStream* pCacheStream = FileStream::OpenFileStream( m_cacheFileName, FileStream::MODE_WRITE, false ); if( !pCacheStream ) { HELIUM_TRACE( TraceLevels::Error, TXT( "Cache: Failed to open cache \"%s\" for writing.\n" ), *m_cacheFileName ); bCacheSuccess = false; } else { HELIUM_TRACE( TraceLevels::Info, TXT( "Cache: Caching \"%s\" to \"%s\" (%" ) TPRIu32 TXT( " bytes @ offset %" ) TPRIu64 TXT( ").\n" ), *path.ToString(), *m_cacheFileName, size, entryOffset ); uint64_t seekOffset = static_cast< uint64_t >( pCacheStream->Seek( static_cast< int64_t >( entryOffset ), SeekOrigins::SEEK_ORIGIN_BEGIN ) ); if( seekOffset != entryOffset ) { HELIUM_TRACE( TraceLevels::Error, TXT( "Cache: Cache file offset seek failed.\n" ) ); if( bNewEntry ) { m_entries.Pop(); m_entryMap.Remove( entryAccessor ); m_pEntryPool->Release( pEntryUpdate ); } else { pEntryUpdate->offset = originalOffset; pEntryUpdate->timestamp = originalTimestamp; pEntryUpdate->size = originalSize; } bCacheSuccess = false; } else { size_t writeSize = pCacheStream->Write( pData, 1, size ); if( writeSize != size ) { HELIUM_TRACE( TraceLevels::Error, ( TXT( "Cache: Failed to write %" ) TPRIu32 TXT( " bytes to cache \"%s\" (%" ) TPRIuSZ TXT( " bytes written).\n" ) ), size, *m_cacheFileName, writeSize ); if( bNewEntry ) { m_entries.Pop(); m_entryMap.Remove( entryAccessor ); m_pEntryPool->Release( pEntryUpdate ); } else { pEntryUpdate->offset = originalOffset; pEntryUpdate->timestamp = originalTimestamp; pEntryUpdate->size = originalSize; } bCacheSuccess = false; } else { HELIUM_TRACE( TraceLevels::Info, TXT( "Cache: Rewriting TOC file \"%s\".\n" ), *m_tocFileName ); FileStream* pTocStream = FileStream::OpenFileStream( m_tocFileName, FileStream::MODE_WRITE, true ); if( !pTocStream ) { HELIUM_TRACE( TraceLevels::Error, TXT( "Cache: Failed to open TOC \"%s\" for writing.\n" ), *m_tocFileName ); } else { BufferedStream* pBufferedStream = new BufferedStream( pTocStream ); HELIUM_ASSERT( pBufferedStream ); pBufferedStream->Write( &TOC_MAGIC, sizeof( TOC_MAGIC ), 1 ); pBufferedStream->Write( &sm_Version, sizeof( sm_Version ), 1 ); uint32_t entryCount = static_cast< uint32_t >( m_entries.GetSize() ); pBufferedStream->Write( &entryCount, sizeof( entryCount ), 1 ); String entryPath; uint_fast32_t entryCountFast = entryCount; for( uint_fast32_t entryIndex = 0; entryIndex < entryCountFast; ++entryIndex ) { Entry* pEntry = m_entries[ entryIndex ]; HELIUM_ASSERT( pEntry ); pEntry->path.ToString( entryPath ); HELIUM_ASSERT( entryPath.GetSize() < UINT16_MAX ); uint16_t pathSize = static_cast< uint16_t >( entryPath.GetSize() ); pBufferedStream->Write( &pathSize, sizeof( pathSize ), 1 ); pBufferedStream->Write( *entryPath, sizeof( tchar_t ), pathSize ); pBufferedStream->Write( &pEntry->subDataIndex, sizeof( pEntry->subDataIndex ), 1 ); pBufferedStream->Write( &pEntry->offset, sizeof( pEntry->offset ), 1 ); pBufferedStream->Write( &pEntry->timestamp, sizeof( pEntry->timestamp ), 1 ); pBufferedStream->Write( &pEntry->size, sizeof( pEntry->size ), 1 ); } delete pBufferedStream; delete pTocStream; } } } delete pCacheStream; } rLoader.Unlock(); return bCacheSuccess; }
/// Initialize all resources provided by this manager. /// /// @see Shutdown(), PostConfigUpdate() void RenderResourceManager::Initialize() { // Release any existing resources. Shutdown(); // Get the renderer and graphics configuration. Renderer* pRenderer = Renderer::GetStaticInstance(); if( !pRenderer ) { return; } Config& rConfig = Config::GetStaticInstance(); StrongPtr< GraphicsConfig > spGraphicsConfig( rConfig.GetConfigObject< GraphicsConfig >( Name( TXT( "GraphicsConfig" ) ) ) ); if( !spGraphicsConfig ) { HELIUM_TRACE( TRACE_ERROR, TXT( "RenderResourceManager::Initialize(): Initialization failed; missing GraphicsConfig.\n" ) ); return; } // Create the standard rasterizer states. RRasterizerState::Description rasterizerStateDesc; rasterizerStateDesc.fillMode = RENDERER_FILL_MODE_SOLID; rasterizerStateDesc.cullMode = RENDERER_CULL_MODE_BACK; rasterizerStateDesc.winding = RENDERER_WINDING_CLOCKWISE; rasterizerStateDesc.depthBias = 0; rasterizerStateDesc.slopeScaledDepthBias = 0.0f; m_rasterizerStates[ RASTERIZER_STATE_DEFAULT ] = pRenderer->CreateRasterizerState( rasterizerStateDesc ); HELIUM_ASSERT( m_rasterizerStates[ RASTERIZER_STATE_DEFAULT ] ); rasterizerStateDesc.cullMode = RENDERER_CULL_MODE_NONE; m_rasterizerStates[ RASTERIZER_STATE_DOUBLE_SIDED ] = pRenderer->CreateRasterizerState( rasterizerStateDesc ); HELIUM_ASSERT( m_rasterizerStates[ RASTERIZER_STATE_DOUBLE_SIDED ] ); rasterizerStateDesc.depthBias = 1; rasterizerStateDesc.slopeScaledDepthBias = 2.0f; m_rasterizerStates[ RASTERIZER_STATE_SHADOW_DEPTH ] = pRenderer->CreateRasterizerState( rasterizerStateDesc ); HELIUM_ASSERT( m_rasterizerStates[ RASTERIZER_STATE_SHADOW_DEPTH ] ); rasterizerStateDesc.depthBias = 0; rasterizerStateDesc.slopeScaledDepthBias = 0.0f; rasterizerStateDesc.fillMode = RENDERER_FILL_MODE_WIREFRAME; m_rasterizerStates[ RASTERIZER_STATE_WIREFRAME_DOUBLE_SIDED ] = pRenderer->CreateRasterizerState( rasterizerStateDesc ); HELIUM_ASSERT( m_rasterizerStates[ RASTERIZER_STATE_WIREFRAME_DOUBLE_SIDED ] ); rasterizerStateDesc.cullMode = RENDERER_CULL_MODE_BACK; m_rasterizerStates[ RASTERIZER_STATE_WIREFRAME ] = pRenderer->CreateRasterizerState( rasterizerStateDesc ); HELIUM_ASSERT( m_rasterizerStates[ RASTERIZER_STATE_WIREFRAME ] ); // Create the standard blend states. RBlendState::Description blendStateDesc; blendStateDesc.bBlendEnable = false; m_blendStates[ BLEND_STATE_OPAQUE ] = pRenderer->CreateBlendState( blendStateDesc ); HELIUM_ASSERT( m_blendStates[ BLEND_STATE_OPAQUE ] ); blendStateDesc.colorWriteMask = 0; m_blendStates[ BLEND_STATE_NO_COLOR ] = pRenderer->CreateBlendState( blendStateDesc ); HELIUM_ASSERT( m_blendStates[ BLEND_STATE_NO_COLOR ] ); blendStateDesc.colorWriteMask = RENDERER_COLOR_WRITE_MASK_FLAG_ALL; blendStateDesc.bBlendEnable = true; blendStateDesc.sourceFactor = RENDERER_BLEND_FACTOR_SRC_ALPHA; blendStateDesc.destinationFactor = RENDERER_BLEND_FACTOR_INV_SRC_ALPHA; blendStateDesc.function = RENDERER_BLEND_FUNCTION_ADD; m_blendStates[ BLEND_STATE_TRANSPARENT ] = pRenderer->CreateBlendState( blendStateDesc ); HELIUM_ASSERT( m_blendStates[ BLEND_STATE_TRANSPARENT ] ); blendStateDesc.sourceFactor = RENDERER_BLEND_FACTOR_ONE; blendStateDesc.destinationFactor = RENDERER_BLEND_FACTOR_ONE; m_blendStates[ BLEND_STATE_ADDITIVE ] = pRenderer->CreateBlendState( blendStateDesc ); HELIUM_ASSERT( m_blendStates[ BLEND_STATE_ADDITIVE ] ); blendStateDesc.function = RENDERER_BLEND_FUNCTION_REVERSE_SUBTRACT; m_blendStates[ BLEND_STATE_SUBTRACTIVE ] = pRenderer->CreateBlendState( blendStateDesc ); HELIUM_ASSERT( m_blendStates[ BLEND_STATE_SUBTRACTIVE ] ); blendStateDesc.sourceFactor = RENDERER_BLEND_FACTOR_DEST_COLOR; blendStateDesc.destinationFactor = RENDERER_BLEND_FACTOR_ZERO; blendStateDesc.function = RENDERER_BLEND_FUNCTION_ADD; m_blendStates[ BLEND_STATE_MODULATE ] = pRenderer->CreateBlendState( blendStateDesc ); HELIUM_ASSERT( m_blendStates[ BLEND_STATE_MODULATE ] ); // Create the standard depth/stencil states. RDepthStencilState::Description depthStateDesc; depthStateDesc.stencilWriteMask = 0; depthStateDesc.bStencilTestEnable = false; depthStateDesc.depthFunction = RENDERER_COMPARE_FUNCTION_LESS_EQUAL; depthStateDesc.bDepthTestEnable = true; depthStateDesc.bDepthWriteEnable = true; m_depthStencilStates[ DEPTH_STENCIL_STATE_DEFAULT ] = pRenderer->CreateDepthStencilState( depthStateDesc ); HELIUM_ASSERT( m_depthStencilStates[ DEPTH_STENCIL_STATE_DEFAULT ] ); depthStateDesc.bDepthWriteEnable = false; m_depthStencilStates[ DEPTH_STENCIL_STATE_TEST_ONLY ] = pRenderer->CreateDepthStencilState( depthStateDesc ); HELIUM_ASSERT( m_depthStencilStates[ DEPTH_STENCIL_STATE_TEST_ONLY ] ); depthStateDesc.bDepthTestEnable = false; m_depthStencilStates[ DEPTH_STENCIL_STATE_NONE ] = pRenderer->CreateDepthStencilState( depthStateDesc ); HELIUM_ASSERT( m_depthStencilStates[ DEPTH_STENCIL_STATE_NONE ] ); // Create the standard sampler states that are not dependent on configuration settings. RSamplerState::Description samplerStateDesc; samplerStateDesc.filter = RENDERER_TEXTURE_FILTER_MIN_POINT_MAG_POINT_MIP_POINT; samplerStateDesc.addressModeW = RENDERER_TEXTURE_ADDRESS_MODE_CLAMP; samplerStateDesc.mipLodBias = 0; samplerStateDesc.maxAnisotropy = spGraphicsConfig->GetMaxAnisotropy(); for( size_t addressModeIndex = 0; addressModeIndex < RENDERER_TEXTURE_ADDRESS_MODE_MAX; ++addressModeIndex ) { ERendererTextureAddressMode addressMode = static_cast< ERendererTextureAddressMode >( addressModeIndex ); samplerStateDesc.addressModeU = addressMode; samplerStateDesc.addressModeV = addressMode; samplerStateDesc.addressModeW = addressMode; m_samplerStates[ TEXTURE_FILTER_POINT ][ addressModeIndex ] = pRenderer->CreateSamplerState( samplerStateDesc ); HELIUM_ASSERT( m_samplerStates[ TEXTURE_FILTER_POINT ][ addressModeIndex ] ); } // Create the standard set of mesh vertex descriptions. RVertexDescription::Element vertexElements[ 6 ]; vertexElements[ 0 ].type = RENDERER_VERTEX_DATA_TYPE_FLOAT32_3; vertexElements[ 0 ].semantic = RENDERER_VERTEX_SEMANTIC_POSITION; vertexElements[ 0 ].semanticIndex = 0; vertexElements[ 0 ].bufferIndex = 0; vertexElements[ 1 ].type = RENDERER_VERTEX_DATA_TYPE_UINT8_4_NORM; vertexElements[ 1 ].semantic = RENDERER_VERTEX_SEMANTIC_COLOR; vertexElements[ 1 ].semanticIndex = 0; vertexElements[ 1 ].bufferIndex = 0; vertexElements[ 2 ].type = RENDERER_VERTEX_DATA_TYPE_FLOAT16_2; vertexElements[ 2 ].semantic = RENDERER_VERTEX_SEMANTIC_TEXCOORD; vertexElements[ 2 ].semanticIndex = 0; vertexElements[ 2 ].bufferIndex = 0; vertexElements[ 3 ].type = RENDERER_VERTEX_DATA_TYPE_FLOAT32_2; vertexElements[ 3 ].semantic = RENDERER_VERTEX_SEMANTIC_TEXCOORD; vertexElements[ 3 ].semanticIndex = 1; vertexElements[ 3 ].bufferIndex = 0; m_spSimpleVertexDescription = pRenderer->CreateVertexDescription( vertexElements, 2 ); HELIUM_ASSERT( m_spSimpleVertexDescription ); m_spSimpleTexturedVertexDescription = pRenderer->CreateVertexDescription( vertexElements, 3 ); HELIUM_ASSERT( m_spSimpleTexturedVertexDescription ); m_spProjectedVertexDescription = pRenderer->CreateVertexDescription( vertexElements, 4 ); HELIUM_ASSERT( m_spProjectedVertexDescription ); vertexElements[ 1 ].type = RENDERER_VERTEX_DATA_TYPE_UINT8_4_NORM; vertexElements[ 1 ].semantic = RENDERER_VERTEX_SEMANTIC_NORMAL; vertexElements[ 1 ].semanticIndex = 0; vertexElements[ 1 ].bufferIndex = 0; vertexElements[ 2 ].type = RENDERER_VERTEX_DATA_TYPE_UINT8_4_NORM; vertexElements[ 2 ].semantic = RENDERER_VERTEX_SEMANTIC_TANGENT; vertexElements[ 2 ].semanticIndex = 0; vertexElements[ 2 ].bufferIndex = 0; vertexElements[ 3 ].type = RENDERER_VERTEX_DATA_TYPE_UINT8_4_NORM; vertexElements[ 3 ].semantic = RENDERER_VERTEX_SEMANTIC_COLOR; vertexElements[ 3 ].semanticIndex = 0; vertexElements[ 3 ].bufferIndex = 0; vertexElements[ 4 ].type = RENDERER_VERTEX_DATA_TYPE_FLOAT16_2; vertexElements[ 4 ].semantic = RENDERER_VERTEX_SEMANTIC_TEXCOORD; vertexElements[ 4 ].semanticIndex = 0; vertexElements[ 4 ].bufferIndex = 0; vertexElements[ 5 ].type = RENDERER_VERTEX_DATA_TYPE_FLOAT16_2; vertexElements[ 5 ].semantic = RENDERER_VERTEX_SEMANTIC_TEXCOORD; vertexElements[ 5 ].semanticIndex = 1; vertexElements[ 5 ].bufferIndex = 0; m_staticMeshVertexDescriptions[ 0 ] = pRenderer->CreateVertexDescription( vertexElements, 5 ); HELIUM_ASSERT( m_staticMeshVertexDescriptions[ 0 ] ); m_staticMeshVertexDescriptions[ 1 ] = pRenderer->CreateVertexDescription( vertexElements, 6 ); HELIUM_ASSERT( m_staticMeshVertexDescriptions[ 1 ] ); vertexElements[ 1 ].type = RENDERER_VERTEX_DATA_TYPE_UINT8_4_NORM; vertexElements[ 1 ].semantic = RENDERER_VERTEX_SEMANTIC_BLENDWEIGHT; vertexElements[ 1 ].semanticIndex = 0; vertexElements[ 1 ].bufferIndex = 0; vertexElements[ 2 ].type = RENDERER_VERTEX_DATA_TYPE_UINT8_4; vertexElements[ 2 ].semantic = RENDERER_VERTEX_SEMANTIC_BLENDINDICES; vertexElements[ 2 ].semanticIndex = 0; vertexElements[ 2 ].bufferIndex = 0; vertexElements[ 3 ].type = RENDERER_VERTEX_DATA_TYPE_UINT8_4_NORM; vertexElements[ 3 ].semantic = RENDERER_VERTEX_SEMANTIC_NORMAL; vertexElements[ 3 ].semanticIndex = 0; vertexElements[ 3 ].bufferIndex = 0; vertexElements[ 4 ].type = RENDERER_VERTEX_DATA_TYPE_UINT8_4_NORM; vertexElements[ 4 ].semantic = RENDERER_VERTEX_SEMANTIC_TANGENT; vertexElements[ 4 ].semanticIndex = 0; vertexElements[ 4 ].bufferIndex = 0; vertexElements[ 5 ].type = RENDERER_VERTEX_DATA_TYPE_FLOAT16_2; vertexElements[ 5 ].semantic = RENDERER_VERTEX_SEMANTIC_TEXCOORD; vertexElements[ 5 ].semanticIndex = 0; vertexElements[ 5 ].bufferIndex = 0; m_spSkinnedMeshVertexDescription = pRenderer->CreateVertexDescription( vertexElements, 6 ); HELIUM_ASSERT( m_spSkinnedMeshVertexDescription ); vertexElements[ 0 ].type = RENDERER_VERTEX_DATA_TYPE_FLOAT32_2; vertexElements[ 0 ].semantic = RENDERER_VERTEX_SEMANTIC_POSITION; vertexElements[ 0 ].semanticIndex = 0; vertexElements[ 0 ].bufferIndex = 0; vertexElements[ 1 ].type = RENDERER_VERTEX_DATA_TYPE_UINT8_4_NORM; vertexElements[ 1 ].semantic = RENDERER_VERTEX_SEMANTIC_COLOR; vertexElements[ 1 ].semanticIndex = 0; vertexElements[ 1 ].bufferIndex = 0; vertexElements[ 2 ].type = RENDERER_VERTEX_DATA_TYPE_FLOAT16_2; vertexElements[ 2 ].semantic = RENDERER_VERTEX_SEMANTIC_TEXCOORD; vertexElements[ 2 ].semanticIndex = 0; vertexElements[ 2 ].bufferIndex = 0; m_spScreenVertexDescription = pRenderer->CreateVertexDescription( vertexElements, 3 ); HELIUM_ASSERT( m_spScreenVertexDescription ); // Create configuration-dependent render resources. PostConfigUpdate(); // Attempt to load the depth-only pre-pass shader. #pragma TODO( "XXX TMC: Migrate to a more data-driven solution." ) GameObjectLoader* pObjectLoader = GameObjectLoader::GetStaticInstance(); HELIUM_ASSERT( pObjectLoader ); GameObjectPath prePassShaderPath; HELIUM_VERIFY( prePassShaderPath.Set( HELIUM_PACKAGE_PATH_CHAR_STRING TXT( "Shaders" ) HELIUM_OBJECT_PATH_CHAR_STRING TXT( "PrePass.hlsl" ) ) ); GameObjectPtr spPrePassShader; HELIUM_VERIFY( pObjectLoader->LoadObject( prePassShaderPath, spPrePassShader ) ); Shader* pPrePassShader = Reflect::SafeCast< Shader >( spPrePassShader.Get() ); HELIUM_ASSERT( pPrePassShader ); if( pPrePassShader ) { size_t loadId = pPrePassShader->BeginLoadVariant( RShader::TYPE_VERTEX, 0 ); HELIUM_ASSERT( IsValid( loadId ) ); if( IsValid( loadId ) ) { while( !pPrePassShader->TryFinishLoadVariant( loadId, m_spPrePassVertexShader ) ) { pObjectLoader->Tick(); } } } // Attempt to load the simple world-space, simple screen-space, and screen-space text shaders. #pragma TODO( "XXX TMC: Migrate to a more data-driven solution." ) GameObjectPath shaderPath; GameObjectPtr spShader; Shader* pShader; HELIUM_VERIFY( shaderPath.Set( HELIUM_PACKAGE_PATH_CHAR_STRING TXT( "Shaders" ) HELIUM_OBJECT_PATH_CHAR_STRING TXT( "Simple.hlsl" ) ) ); HELIUM_VERIFY( pObjectLoader->LoadObject( shaderPath, spShader ) ); pShader = Reflect::SafeCast< Shader >( spShader.Get() ); HELIUM_ASSERT( pShader ); if( pShader ) { size_t loadId = pShader->BeginLoadVariant( RShader::TYPE_VERTEX, 0 ); HELIUM_ASSERT( IsValid( loadId ) ); if( IsValid( loadId ) ) { while( !pShader->TryFinishLoadVariant( loadId, m_spSimpleWorldSpaceVertexShader ) ) { pObjectLoader->Tick(); } } loadId = pShader->BeginLoadVariant( RShader::TYPE_PIXEL, 0 ); HELIUM_ASSERT( IsValid( loadId ) ); if( IsValid( loadId ) ) { while( !pShader->TryFinishLoadVariant( loadId, m_spSimpleWorldSpacePixelShader ) ) { pObjectLoader->Tick(); } } } HELIUM_VERIFY( shaderPath.Set( HELIUM_PACKAGE_PATH_CHAR_STRING TXT( "Shaders" ) HELIUM_OBJECT_PATH_CHAR_STRING TXT( "ScreenSpaceTexture.hlsl" ) ) ); HELIUM_VERIFY( pObjectLoader->LoadObject( shaderPath, spShader ) ); pShader = Reflect::SafeCast< Shader >( spShader.Get() ); HELIUM_ASSERT( pShader ); if( pShader ) { size_t loadId = pShader->BeginLoadVariant( RShader::TYPE_VERTEX, 0 ); HELIUM_ASSERT( IsValid( loadId ) ); if( IsValid( loadId ) ) { while( !pShader->TryFinishLoadVariant( loadId, m_spSimpleScreenSpaceVertexShader ) ) { pObjectLoader->Tick(); } } loadId = pShader->BeginLoadVariant( RShader::TYPE_PIXEL, 0 ); HELIUM_ASSERT( IsValid( loadId ) ); if( IsValid( loadId ) ) { while( !pShader->TryFinishLoadVariant( loadId, m_spSimpleScreenSpacePixelShader ) ) { pObjectLoader->Tick(); } } } HELIUM_VERIFY( shaderPath.Set( HELIUM_PACKAGE_PATH_CHAR_STRING TXT( "Shaders" ) HELIUM_OBJECT_PATH_CHAR_STRING TXT( "ScreenText.hlsl" ) ) ); HELIUM_VERIFY( pObjectLoader->LoadObject( shaderPath, spShader ) ); pShader = Reflect::SafeCast< Shader >( spShader.Get() ); HELIUM_ASSERT( pShader ); if( pShader ) { size_t loadId = pShader->BeginLoadVariant( RShader::TYPE_VERTEX, 0 ); HELIUM_ASSERT( IsValid( loadId ) ); if( IsValid( loadId ) ) { while( !pShader->TryFinishLoadVariant( loadId, m_spScreenTextVertexShader ) ) { pObjectLoader->Tick(); } } loadId = pShader->BeginLoadVariant( RShader::TYPE_PIXEL, 0 ); HELIUM_ASSERT( IsValid( loadId ) ); if( IsValid( loadId ) ) { while( !pShader->TryFinishLoadVariant( loadId, m_spScreenTextPixelShader ) ) { pObjectLoader->Tick(); } } } // Attempt to load the debug fonts. #pragma TODO( "XXX TMC: Migrate to a more data-driven solution." ) GameObjectPath fontPath; GameObjectPtr spFont; HELIUM_VERIFY( fontPath.Set( HELIUM_PACKAGE_PATH_CHAR_STRING TXT( "Fonts" ) HELIUM_OBJECT_PATH_CHAR_STRING TXT( "DebugSmall" ) ) ); HELIUM_VERIFY( pObjectLoader->LoadObject( fontPath, spFont ) ); m_debugFonts[ DEBUG_FONT_SIZE_SMALL ] = Reflect::SafeCast< Font >( spFont.Get() ); spFont.Release(); HELIUM_VERIFY( fontPath.Set( HELIUM_PACKAGE_PATH_CHAR_STRING TXT( "Fonts" ) HELIUM_OBJECT_PATH_CHAR_STRING TXT( "DebugMedium" ) ) ); HELIUM_VERIFY( pObjectLoader->LoadObject( fontPath, spFont ) ); m_debugFonts[ DEBUG_FONT_SIZE_MEDIUM ] = Reflect::SafeCast< Font >( spFont.Get() ); spFont.Release(); HELIUM_VERIFY( fontPath.Set( HELIUM_PACKAGE_PATH_CHAR_STRING TXT( "Fonts" ) HELIUM_OBJECT_PATH_CHAR_STRING TXT( "DebugLarge" ) ) ); HELIUM_VERIFY( pObjectLoader->LoadObject( fontPath, spFont ) ); m_debugFonts[ DEBUG_FONT_SIZE_LARGE ] = Reflect::SafeCast< Font >( spFont.Get() ); spFont.Release(); }
/// @copydoc GameObjectLoader::CacheObject() bool EditorObjectLoader::CacheObject( GameObject* pObject, bool bEvictPlatformPreprocessedResourceData ) { HELIUM_ASSERT( pObject ); // Don't cache broken objects or packages. if( pObject->GetAnyFlagSet( GameObject::FLAG_BROKEN ) || pObject->IsPackage() ) { return false; } // Make sure we have an object preprocessor instance with which to cache the object. ObjectPreprocessor* pObjectPreprocessor = ObjectPreprocessor::GetStaticInstance(); if( !pObjectPreprocessor ) { HELIUM_TRACE( TRACE_WARNING, TXT( "EditorObjectLoader::CacheObject(): Missing ObjectPreprocessor to use for caching.\n" ) ); return false; } // Configuration objects should not be cached. GameObjectPath objectPath = pObject->GetPath(); Config& rConfig = Config::GetStaticInstance(); GameObjectPath configPackagePath = rConfig.GetConfigContainerPackagePath(); HELIUM_ASSERT( !configPackagePath.IsEmpty() ); for( GameObjectPath testPath = objectPath; !testPath.IsEmpty(); testPath = testPath.GetParent() ) { if( testPath == configPackagePath ) { return false; } } // Get the timestamp for the object based on the timestamp of its source package file and, if it's a resource, // the timestamp of the source resource file. GameObject* pPackageObject; for( pPackageObject = pObject; pPackageObject && !pPackageObject->IsPackage(); pPackageObject = pPackageObject->GetOwner() ) { } HELIUM_ASSERT( pPackageObject ); PackageLoader* pPackageLoader = Reflect::AssertCast< Package >( pPackageObject )->GetLoader(); HELIUM_ASSERT( pPackageLoader ); HELIUM_ASSERT( pPackageLoader->IsSourcePackageFile() ); int64_t objectTimestamp = pPackageLoader->GetFileTimestamp(); if( !pObject->IsDefaultTemplate() ) { Resource* pResource = Reflect::SafeCast< Resource >( pObject ); if( pResource ) { GameObjectPath baseResourcePath = pResource->GetPath(); HELIUM_ASSERT( !baseResourcePath.IsPackage() ); for( ; ; ) { GameObjectPath parentPath = baseResourcePath.GetParent(); if( parentPath.IsEmpty() || parentPath.IsPackage() ) { break; } baseResourcePath = parentPath; } Path sourceFilePath; if ( !File::GetDataDirectory( sourceFilePath ) ) { HELIUM_TRACE( TRACE_WARNING, TXT( "EditorObjectLoader::CacheObject(): Could not obtain data directory.\n" ) ); return false; } sourceFilePath += baseResourcePath.ToFilePathString().GetData(); int64_t sourceFileTimestamp = sourceFilePath.ModifiedTime(); if( sourceFileTimestamp > objectTimestamp ) { objectTimestamp = sourceFileTimestamp; } } } // Cache the object. bool bSuccess = pObjectPreprocessor->CacheObject( pObject, objectTimestamp, bEvictPlatformPreprocessedResourceData ); if( !bSuccess ) { HELIUM_TRACE( TRACE_ERROR, TXT( "EditorObjectLoader: Failed to cache object \"%s\".\n" ), *objectPath.ToString() ); } return bSuccess; }