Esempio n. 1
0
/// 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;
}
Esempio n. 2
0
/// 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 AssetLoader::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.
		AssetPath path = pRequest->path;
		pRequest->packageLoadRequestId = pPackageLoader->BeginLoadObject( path, &pRequest->resolver );
		if( IsInvalid( pRequest->packageLoadRequestId ) )
		{
			pRequest->spObject = Asset::FindObject( path );
			Asset* pObject = pRequest->spObject;
			if( pObject )
			{
				HELIUM_TRACE(
					TraceLevels::Info,
					TXT( "AssetLoader: Asset \"%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( Asset::FLAG_PRELOADED | Asset::FLAG_LINKED );

				AtomicOrRelease( pRequest->stateFlags, LOAD_FLAG_PRELOADED | LOAD_FLAG_LINKED );

				return true;
			}

			HELIUM_TRACE(
				TraceLevels::Error,
				TXT( "AssetLoader: Asset \"%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 );
	if( !bFinished )
	{
		// Still waiting for object to load.
		return false;
	}

	// Preload complete.
	SetInvalid( pRequest->packageLoadRequestId );

	AtomicOrRelease( pRequest->stateFlags, LOAD_FLAG_PRELOADED );

	return true;
}
Esempio n. 3
0
/// Begin asynchronous loading of an object.
///
/// @param[in] path  Asset path.
///
/// @return  ID for the load request if started successfully, invalid index if not.
///
/// @see TryFinishLoad(), FinishLoad()
size_t AssetLoader::BeginLoadObject( AssetPath path )
{
	HELIUM_TRACE( TraceLevels::Info, TXT(" AssetLoader::BeginLoadObject - Loading path %s\n"), *path.ToString() );

	// Search for an existing load request with the given path.
	ConcurrentHashMap< AssetPath, 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 );
	}

	Asset *pAsset = Asset::Find<Asset>( path );
	if ( pAsset && !pAsset->GetAllFlagsSet( Asset::FLAG_LOADED ) )
	{
		pAsset = NULL;
	}

	PackageLoader *pPackageLoader = 0;
	if ( pAsset )
	{
			HELIUM_TRACE(
				TraceLevels::Info,
				TXT( "AssetLoader::BeginLoadObject(): Object \"%s\" already loaded.\n" ),
				*path.ToString() );
	} 
	else
	{
		// Get the package loader to use for the given object.
		pPackageLoader = GetPackageLoader( path );
		if( !pPackageLoader )
		{
			HELIUM_TRACE(
				TraceLevels::Error,
				TXT( "AssetLoader::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;
	pRequest->pPackageLoader = pPackageLoader;
	SetInvalid( pRequest->packageLoadRequestId );
	pRequest->stateFlags = pAsset ? 
		(pAsset->GetFlags() & Asset::FLAG_BROKEN ? LOAD_FLAG_FULLY_LOADED | LOAD_FLAG_ERROR : LOAD_FLAG_FULLY_LOADED ) : 
		0;
	pRequest->requestCount = 1;
	HELIUM_ASSERT( !pRequest->spObject );
	pRequest->spObject = pAsset;

	ConcurrentHashMap< AssetPath, LoadRequest* >::Accessor requestAccessor;
	if( m_loadRequestMap.Insert( requestAccessor, KeyValue< AssetPath, 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 );
}
Esempio n. 4
0
/// Initialize this package loader.
///
/// @param[in] packagePath  Asset path of the package to load.
///
/// @return  True if this loader was initialized successfully, false if not.
///
/// @see Shutdown()
bool LoosePackageLoader::Initialize( AssetPath packagePath )
{
	Shutdown();

	// Make sure the path represents a package.
	if( packagePath.IsEmpty() )
	{
		HELIUM_TRACE( TraceLevels::Error, TXT( "LoosePackageLoader::Initialize(): Empty package path specified.\n" ) );

		return false;
	}

	HELIUM_TRACE(
		TraceLevels::Debug,
		TXT( "LoosePackageLoader::Initialize(): Initializing loader for package \"%s\".\n" ),
		*packagePath.ToString() );

	if( !packagePath.IsPackage() )
	{
		HELIUM_TRACE(
			TraceLevels::Error,
			TXT( "LoosePackageLoader::Initialize(): \"%s\" does not represent a package path.\n" ),
			*packagePath.ToString() );

		return false;
	}

	// Store the package path.
	m_packagePath = packagePath;

	// Attempt to locate the specified package if it already happens to exist.
	m_spPackage = Asset::Find< Package >( packagePath );
	Package* pPackage = m_spPackage;
	if( pPackage )
	{
		if( pPackage->GetLoader() )
		{
			HELIUM_TRACE(
				TraceLevels::Error,
				TXT( "LoosePackageLoader::Initialize(): Package \"%s\" already has a loader.\n" ),
				*packagePath.ToString() );

			m_spPackage.Release();

			return false;
		}

		pPackage->SetLoader( this );
	}
	else
	{
		// Make sure we don't have a name clash with a non-package object.
		AssetPtr spObject( Asset::FindObject( packagePath ) );
		if( spObject )
		{
			HELIUM_ASSERT( !spObject->IsPackage() );

			HELIUM_TRACE(
				TraceLevels::Error,
				( TXT( "PackageLoader::Initialize(): Package loader cannot be initialized for \"%s\", as an " )
				TXT( "object with the same name exists that is not a package.\n" ) ),
				*packagePath.ToString() );

			return false;
		}
	}

	// Build the package file path.  If the package is a user configuration package, use the user data directory,
	// otherwise use the global data directory.
	Config& rConfig = Config::GetStaticInstance();
	FilePath dataDirectory;

	if ( !FileLocations::GetDataDirectory( dataDirectory ) )
	{
		HELIUM_TRACE(
			TraceLevels::Error,
			TXT( "PackageLoader::Initialize(): Could not obtain user data directory." ) );

		return false;
	}

	// Set up to read the TOC (which may not exist)
	//SetInvalid( m_packageTocFileSize );

	// First do this check without a trailing "/" so that FilePath has to actually look at the file system
	FilePath package_dir = dataDirectory + packagePath.ToFilePathString().GetData();
	
	if (!package_dir.Exists())
	{
		// Some packages like types or uninitialized user config packages may not exist on file system
		m_packageDirPath = package_dir + TXT("/");
		return true;
	}

	if (!package_dir.IsDirectory())
	{
		// Packages should not be files
		return false;
	}
	
	// But internally we will store this 
	m_packageDirPath = package_dir + TXT("/");

	return true;
}
Esempio n. 5
0
/// @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;
}
/// @copydoc ResourceHandler::CacheResource()
bool ShaderResourceHandler::CacheResource(
    AssetPreprocessor* pAssetPreprocessor,
    Resource* pResource,
    const String& rSourceFilePath )
{
    HELIUM_ASSERT( pAssetPreprocessor );
    HELIUM_ASSERT( pResource );

    const Shader* pShader = Reflect::AssertCast< const Shader >( pResource );
    AssetPath 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 %" ) PRIuSZ 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.
    StrongPtr< Shader::PersistentResourceData > resourceData( new Shader::PersistentResourceData() );

    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 = pAssetPreprocessor->GetPlatformPreprocessor(
            static_cast< Cache::EPlatform >( platformIndex ) );
        if( !pPreprocessor )
        {
            continue;
        }

        Resource::PreprocessedData& rPreprocessedData = pResource->GetPreprocessedData(
            static_cast< Cache::EPlatform >( platformIndex ) );
        SaveObjectToPersistentDataBuffer(resourceData.Get(), 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     Asset 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(
                                      AssetPath 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, char >::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, char >::Convert( convertedString, splitLine[ choiceIndex ] ) ) );
            pSelect->choices.New( convertedString );
        }
    }
}
/// Deserialize the link tables for an object load.
///
/// @param[in] pRequest  Load request data.
bool CachePackageLoader::ReadCacheData( LoadRequest* pRequest )
{
	HELIUM_ASSERT( pRequest );

	uint8_t* pBufferCurrent = pRequest->pAsyncLoadBuffer;
	uint8_t* pPropertyDataEnd = pRequest->pPropertyDataEnd;
	HELIUM_ASSERT( pBufferCurrent );
	HELIUM_ASSERT( pPropertyDataEnd );
	HELIUM_ASSERT( pBufferCurrent <= pPropertyDataEnd );

	// We know the owner's path immediately just by looking at the path we're currently loading
	AssetPath parentPath = pRequest->pEntry->path.GetParent();
	pRequest->ownerLoadIndex = AssetLoader::GetInstance()->BeginLoadObject( parentPath );

	if (IsInvalid<size_t>(pRequest->ownerLoadIndex))
	{
		HELIUM_TRACE(
			TraceLevels::Debug,
			"CachePackageLoader: Failed to begin loading owning asset '%s' for '%s'.\n",
			*parentPath.ToString(),
			*pRequest->pEntry->path.ToString());
	}

	// Scan the property stream.. another null terminated string with character count first
	uint32_t propertyStreamSize = 0;
	if( pBufferCurrent + sizeof( propertyStreamSize ) > pPropertyDataEnd )
	{
		HELIUM_TRACE(
			TraceLevels::Error,
			"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 >( pPropertyDataEnd - pBufferCurrent ) )
	{
		HELIUM_TRACE(
			TraceLevels::Error,
			"CachePackageLoader: Property stream size (%" PRIu32 " bytes) for \"%s\" exceeds the amount of data cached.  Value will be clamped.\n",
			propertyStreamSize,
			*pRequest->pEntry->path.ToString() );

		propertyStreamSize = static_cast< uint32_t >( pPropertyDataEnd - pBufferCurrent );
	}

	pRequest->pPropertyDataBegin = pBufferCurrent;
	pPropertyDataEnd = pBufferCurrent + propertyStreamSize;
	pRequest->pPropertyDataEnd = pPropertyDataEnd;

	// Verify that it's null terminated
	HELIUM_ASSERT( pRequest->pPropertyDataEnd[-1] == 0);

	// 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). The count is at the END of the buffer, and
	// if we have enough room for a size left, then assume persistant resource starts immediately after the property
	// data ends, and ends at the count.
	if( pRequest->pPersistentResourceDataEnd - pPropertyDataEnd >= sizeof( uint32_t ) )
	{
		pRequest->pPersistentResourceDataBegin = pPropertyDataEnd;
		pRequest->pPersistentResourceDataEnd -= sizeof( uint32_t );

		HELIUM_ASSERT( pRequest->pPersistentResourceDataEnd[-1] == 0 );
	}
	else
	{
		pRequest->pPersistentResourceDataBegin = pPropertyDataEnd;
		pRequest->pPersistentResourceDataEnd = pPropertyDataEnd;
	}

	return true;
}
Esempio n. 9
0
/// Initialize this system.
///
/// @param[in] rCommandLineInitialization    Interface for initializing command-line parameters.
/// @param[in] rMemoryHeapPreInitialization  Interface for performing any necessary pre-initialization of dynamic
///                                          memory heaps.
/// @param[in] rAssetLoaderInitialization   Interface for creating and initializing the main AssetLoader instance.
///                                          Note that this must remain valid until Shutdown() is called on this
///                                          system, as a reference to it will be held by this system.
/// @param[in] rConfigInitialization         Interface for initializing application configuration settings.
/// @param[in] rWindowManagerInitialization  Interface for creating and initializing the global window manager
///                                          instance.
/// @param[in] rRendererInitialization       Interface for creating and initializing the global renderer instance.
/// @param[in] pWorldType                    Type of World to create for the main world.  If this is null, the
///                                          actual World type will be used.
bool GameSystem::Initialize(
	CommandLineInitialization& rCommandLineInitialization,
	MemoryHeapPreInitialization& rMemoryHeapPreInitialization,
	AssetLoaderInitialization& rAssetLoaderInitialization,
	ConfigInitialization& rConfigInitialization,
	WindowManagerInitialization& rWindowManagerInitialization,
	RendererInitialization& rRendererInitialization,
	AssetPath &rSystemDefinitionPath)
{
	// Initialize the timer first of all, in case someone wants to use it.
	Timer::StaticInitialize();

	// Initialize command-line parameters.
	bool bCommandLineInitSuccess = rCommandLineInitialization.Initialize( m_moduleName, m_arguments );
	HELIUM_ASSERT( bCommandLineInitSuccess );
	if( !bCommandLineInitSuccess )
	{
		HELIUM_TRACE( TraceLevels::Error, TXT( "GameSystem::Initialize(): Command-line initialization failed.\n" ) );

		return false;
	}

#if HELIUM_ENABLE_TRACE
	HELIUM_TRACE( TraceLevels::Info, TXT( "Module name: %s\n" ), *m_moduleName );
	HELIUM_TRACE( TraceLevels::Info, TXT( "Command-line arguments:\n" ) );
	size_t argumentCount = m_arguments.GetSize();
	for( size_t argumentIndex = 0; argumentIndex < argumentCount; ++argumentIndex )
	{
		HELIUM_TRACE( TraceLevels::Info, TXT( "* %s\n" ), *m_arguments[ argumentIndex ] );
	}
#endif


	// Initialize the async loading thread.
	bool bAsyncLoaderInitSuccess = AsyncLoader::GetStaticInstance().Initialize();
	HELIUM_ASSERT( bAsyncLoaderInitSuccess );
	if( !bAsyncLoaderInitSuccess )
	{
		HELIUM_TRACE( TraceLevels::Error, TXT( "GameSystem::Initialize(): Async loader initialization failed.\n" ) );

		return false;
	}

	//pmd - Initialize the cache manager
	FilePath baseDirectory;
	if ( !FileLocations::GetBaseDirectory( baseDirectory ) )
	{
	  HELIUM_TRACE( TraceLevels::Error, TXT( "Could not get base directory." ) );
	  return false;
	}

	HELIUM_VERIFY( CacheManager::InitializeStaticInstance( baseDirectory ) );

	// Initialize the reflection type registry and register Asset-based types.
	Reflect::Initialize();
	
	Components::Initialize();
	
	TaskScheduler::CalculateSchedule();

	// Perform dynamic memory heap pre-initialization.
	rMemoryHeapPreInitialization.PreInitialize();

	// Create and initialize the main AssetLoader instance.
	AssetLoader* pAssetLoader = rAssetLoaderInitialization.Initialize();
	HELIUM_ASSERT( pAssetLoader );
	if( !pAssetLoader )
	{
		HELIUM_TRACE( TraceLevels::Error, TXT( "GameSystem::Initialize(): Asset loader initialization failed.\n" ) );

		return false;
	}

	m_pAssetLoaderInitialization = &rAssetLoaderInitialization;

	// Initialize system configuration.
	bool bConfigInitSuccess = rConfigInitialization.Initialize();
	HELIUM_ASSERT( bConfigInitSuccess );
	if( !bConfigInitSuccess )
	{
		HELIUM_TRACE( TraceLevels::Error, TXT( "GameSystem::Initialize(): Failed to initialize configuration settings.\n" ) );

		return false;
	}

	if ( !rSystemDefinitionPath.IsEmpty() )
	{
		pAssetLoader->LoadObject<SystemDefinition>( rSystemDefinitionPath, m_spSystemDefinition );
		if ( !m_spSystemDefinition )
		{
			HELIUM_TRACE( TraceLevels::Error, TXT( "GameSystem::Initialize(): Could not find SystemDefinition. LoadObject on '%s' failed.\n" ), *rSystemDefinitionPath.ToString() );
		}
		else
		{
			m_spSystemDefinition->Initialize();
		}
	}

	// Initialize the job manager.
	bool bJobManagerInitSuccess = JobManager::GetStaticInstance().Initialize();
	HELIUM_ASSERT( bJobManagerInitSuccess );
	if( !bJobManagerInitSuccess )
	{
		HELIUM_TRACE( TraceLevels::Error, TXT( "GameSystem::Initialize(): Job manager initialization failed.\n" ) );

		return false;
	}

	// Create and initialize the window manager (note that we need a window manager for message loop processing, so
	// the instance cannot be left null).
	bool bWindowManagerInitSuccess = rWindowManagerInitialization.Initialize();
	HELIUM_ASSERT( bWindowManagerInitSuccess );
	if( !bWindowManagerInitSuccess )
	{
		HELIUM_TRACE( TraceLevels::Error, TXT( "GameSystem::Initialize(): Window manager initialization failed.\n" ) );

		return false;
	}
	
	// Create and initialize the renderer.
	bool bRendererInitSuccess = rRendererInitialization.Initialize();
	HELIUM_ASSERT( bRendererInitSuccess );
	if( !bRendererInitSuccess )
	{
		HELIUM_TRACE( TraceLevels::Error, TXT( "GameSystem::Initialize(): Renderer initialization failed.\n" ) );

		return false;
	}

	m_pRendererInitialization = &rRendererInitialization;
	
	// Initialize the world manager and main game world.
	WorldManager& rWorldManager = WorldManager::GetStaticInstance();
	bool bWorldManagerInitSuccess = rWorldManager.Initialize();
	HELIUM_ASSERT( bWorldManagerInitSuccess );
	if( !bWorldManagerInitSuccess )
	{
		HELIUM_TRACE( TraceLevels::Error, TXT( "World manager initialization failed.\n" ) );

		return false;
	}

	// Initialization complete.
	return true;
}
Esempio n. 10
0
/// @copydoc PackageLoader::BeginLoadObject()
size_t CachePackageLoader::BeginLoadObject( AssetPath path, Reflect::ObjectResolver *pResolver, bool forceReload )
{
	HELIUM_ASSERT( m_pCache );
	HELIUM_ASSERT( !forceReload ); // Not supported

	// Don't load packages from the cache, but instead create them dynamically.
	if( path.IsPackage() )
	{
		HELIUM_TRACE(
			TraceLevels::Debug,
			"CachePackageLoader::BeginLoadObject(): \"%s\" is a package, resolving immediately.\n",
			*path.ToString() );

		LoadRequest* pRequest = m_loadRequestPool.Allocate();
		HELIUM_ASSERT( pRequest );
		pRequest->pEntry = NULL;
		pRequest->pResolver = pResolver;

		ResolvePackage( pRequest->spObject, path );
		HELIUM_ASSERT( pRequest->spObject );

		SetInvalid( pRequest->asyncLoadId );
		pRequest->pAsyncLoadBuffer = NULL;
		pRequest->pPropertyDataBegin = NULL;
		pRequest->pPropertyDataEnd = NULL;
		pRequest->pPersistentResourceDataBegin = NULL;
		pRequest->pPersistentResourceDataEnd = NULL;
		SetInvalid( pRequest->ownerLoadIndex );
		HELIUM_ASSERT( !pRequest->spOwner );
		pRequest->forceReload = forceReload;

		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,
			"CachePackageLoader::BeginLoadObject(): \"%s\" is not cached in this package.  No load 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,
				"CachePackageLoader::BeginLoadObject(): Duplicate load request of \"%s\".  No load request added.\n",
				*path.ToString() );

			return Invalid< size_t >();
		}
	}
#endif

	LoadRequest* pRequest = m_loadRequestPool.Allocate();
	HELIUM_ASSERT( pRequest );
	pRequest->pEntry = pEntry;
	pRequest->pResolver = pResolver;
	HELIUM_ASSERT( !pRequest->spObject );
	SetInvalid( pRequest->asyncLoadId );
	pRequest->pAsyncLoadBuffer = NULL;
	pRequest->pPropertyDataBegin = NULL;
	pRequest->pPropertyDataEnd = NULL;
	pRequest->pPersistentResourceDataBegin = NULL;
	pRequest->pPersistentResourceDataEnd = NULL;
	SetInvalid( pRequest->ownerLoadIndex );
	HELIUM_ASSERT( !pRequest->spOwner );
	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).
	pRequest->spObject = Asset::FindObject( pEntry->path );

	Asset* pObject = pRequest->spObject;
	if( pObject && pObject->IsFullyLoaded() )
	{
		HELIUM_TRACE(
			TraceLevels::Debug,
			"CachePackageLoader::BeginLoadObject(): \"%s\" is already fully loaded.  Bypassing load process.\n",
			*path.ToString() );

		pRequest->flags = LOAD_FLAG_PRELOADED;
	}
	else
	{
		HELIUM_ASSERT( !pObject || !pObject->GetAnyFlagSet( Asset::FLAG_LOADED | Asset::FLAG_LINKED ) );

		HELIUM_TRACE(
			TraceLevels::Debug,
			"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* pAsyncLoader = AsyncLoader::GetInstance();
		HELIUM_ASSERT( pAsyncLoader );

		pRequest->asyncLoadId = pAsyncLoader->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,
		"CachePackageLoader::BeginLoadObject(): Load request for \"%s\" added (ID: %" PRIuSZ ").\n",
		*path.ToString(),
		requestId );

	return requestId;
}
Esempio n. 11
0
    /// 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;
    }
Esempio n. 12
0
/// @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;
}
Esempio n. 13
0
/// 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;
}
Esempio n. 14
0
/// Add or update an entry in the cache.
///
/// @param[in] path          Asset 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(
					   AssetPath 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\" (%" ) PRIu32 TXT( " bytes @ offset %" ) PRIu64 TXT( ").\n" ),
			*path.ToString(),
			*m_cacheFileName,
			size,
			entryOffset );

		uint64_t seekOffset = static_cast< uint64_t >( pCacheStream->Seek(
			static_cast< int64_t >( entryOffset ),
			SeekOrigins::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 %" ) PRIu32 TXT( " bytes to cache \"%s\" (%" ) PRIuSZ
					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( char ), 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;
}