Example #1
0
/// 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 );
}
Example #2
0
/// 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;
}
Example #3
0
/// 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;
}
Example #4
0
/// 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;
}
Example #5
0
/// 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 );
}
Example #6
0
/// 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 );
}
Example #7
0
/// 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;
}
Example #8
0
/// 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 );
        }
    }
}
Example #11
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 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;
}
Example #12
0
/// 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;
}
Example #13
0
/// @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;
}
Example #14
0
/// 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;
}
Example #15
0
/// 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;
}
Example #16
0
/// 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();
}
Example #17
0
/// @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;
}