Example #1
0
void SimpleObjectSortedMapData< KeyT, CompareKeyT, AllocatorT >::Serialize( ArchiveT& archive )
{
    DynArray< ObjectPtr > components;
    components.Reserve( m_Data->GetSize() * 2 );

    {
        DataType::ConstIterator itr = m_Data->Begin();
        DataType::ConstIterator end = m_Data->End();
        for ( ; itr != end; ++itr )
        {
            ObjectPtr elem = Registry::GetInstance()->CreateInstance( Reflect::GetDataClass< KeyT >() );

            Data* ser = AssertCast< Data >( elem.Ptr() );
            ser->ConnectData( const_cast< KeyT* >( &itr->First() ) );

            HELIUM_VERIFY( components.New( ser ) );
            HELIUM_VERIFY( components.New( itr->Second() ) );
        }
    }

    archive.SerializeArray( components );

    {
        DynArray< ObjectPtr >::Iterator itr = components.Begin();
        DynArray< ObjectPtr >::Iterator end = components.End();
        for ( ; itr != end; ++itr )
        {
            Data* ser = AssertCast< Data >( *itr );
            ser->Disconnect();
            ++itr;  // Skip over the object reference.

            // might be useful to cache the data object here
        }
    }
}
Example #2
0
/// Resolve a set of shader preprocessor options from the associated index.
///
/// @param[in]  shaderType    Type of shader.
/// @param[in]  index         Option set index.
/// @param[out] rToggleNames  List of enabled shader toggles.
/// @param[out] rSelectPairs  List shader selection pair values.
void Shader::Options::GetOptionSetFromIndex(
    RShader::EType shaderType,
    size_t index,
    DynArray< Name >& rToggleNames,
    DynArray< SelectPair >& rSelectPairs ) const
{
    HELIUM_ASSERT( static_cast< size_t >( shaderType ) < static_cast< size_t >( RShader::TYPE_MAX ) );

    rToggleNames.Resize( 0 );
    rSelectPairs.Resize( 0 );

    uint32_t shaderTypeMask = ( 1 << shaderType );

    size_t shaderToggleCount = m_toggles.GetSize();
    for( size_t shaderToggleIndex = 0; shaderToggleIndex < shaderToggleCount; ++shaderToggleIndex )
    {
        const Toggle& rShaderToggle = m_toggles[ shaderToggleIndex ];
        if( !( rShaderToggle.shaderTypeFlags & shaderTypeMask ) )
        {
            continue;
        }

        if( index & 0x1 )
        {
            HELIUM_VERIFY( rToggleNames.New( rShaderToggle.name ) );
        }

        index >>= 1;
    }

    size_t shaderSelectCount = m_selects.GetSize();
    for( size_t shaderSelectIndex = 0; shaderSelectIndex < shaderSelectCount; ++shaderSelectIndex )
    {
        const Select& rShaderSelect = m_selects[ shaderSelectIndex ];
        if( !( rShaderSelect.shaderTypeFlags & shaderTypeMask ) )
        {
            continue;
        }

        const DynArray< Name >& rShaderSelectChoices = rShaderSelect.choices;
        size_t shaderSelectChoiceCount = rShaderSelectChoices.GetSize();

        size_t selectIndexMultiplier = shaderSelectChoiceCount + rShaderSelect.bOptional;

        size_t selectIndex = index % selectIndexMultiplier;
        index /= selectIndexMultiplier;

        if( !rShaderSelect.bOptional || selectIndex != shaderSelectChoiceCount )
        {
            SelectPair* pSelectPair = rSelectPairs.New();
            HELIUM_ASSERT( pSelectPair );
            pSelectPair->name = rShaderSelect.name;
            pSelectPair->choice = rShaderSelectChoices[ selectIndex ];
        }
    }
}
Example #3
0
void SimpleSortedSetData< KeyT, CompareKeyT, AllocatorT >::Serialize( ArchiveT& archive )
{
    DynArray< ObjectPtr > components;
    components.Reserve( m_Data->GetSize() );

    {
        DataType::ConstIterator itr = m_Data->Begin();
        DataType::ConstIterator end = m_Data->End();
        for ( ; itr != end; ++itr )
        {
            ObjectPtr dataElem = Registry::GetInstance()->CreateInstance( Reflect::GetDataClass< KeyT >() );

            // downcast to data type
            Data* dataSer = AssertCast< Data >( dataElem );

            // connect to our map data memory address
            dataSer->ConnectData( const_cast< KeyT* >( &( *itr ) ) );

            // serialize to the archive stream
            HELIUM_VERIFY( components.New( dataSer ) );
        }
    }

    archive.SerializeArray( components );

    DynArray< ObjectPtr >::Iterator itr = components.Begin();
    DynArray< ObjectPtr >::Iterator end = components.End();
    for ( ; itr != end; ++itr )
    {
        Data* ser = AssertCast< Data >( *itr );
        ser->Disconnect();

        // might be useful to cache the data object here
    }
}
Example #4
0
void SimpleSortedSetData< KeyT, CompareKeyT, AllocatorT >::GetItems( DynArray< DataPtr >& items ) const
{
    items.Clear();
    items.Reserve( m_Data->GetSize() );

    DataType::ConstIterator itr = m_Data->Begin();
    DataType::ConstIterator end = m_Data->End();
    for ( ; itr != end; ++itr )
    {
        HELIUM_VERIFY( items.New( Data::Bind( const_cast< KeyT& >( *itr ), m_Instance, m_Field ) ) );
    }
}
Example #5
0
/// Compress a font texture sheet and add the texture data to the given texture sheet array.
///
/// @param[in] pGrayscaleData  Texture sheet data, stored as a contiguous array of 8-bit grayscale values.
/// @param[in] textureWidth    Width of the texture sheet.
/// @param[in] textureHeight   Height of the texture sheet.
/// @param[in] compression     Font texture sheet compression method to use.
/// @param[in] rTextureSheets  Array of texture sheets to which the texture data should be appended.
void FontResourceHandler::CompressTexture(
    const uint8_t* pGrayscaleData,
    uint16_t textureWidth,
    uint16_t textureHeight,
    Font::ECompression compression,
    DynArray< DynArray< uint8_t > >& rTextureSheets )
{
    HELIUM_ASSERT( pGrayscaleData );

    DynArray< uint8_t >* pOutputSheet = rTextureSheets.New();
    HELIUM_ASSERT( pOutputSheet );

    // If the output is to be uncompressed grayscale data, simply copy the data to the output texture, as it's already
    // uncompressed grayscale data.
    if( compression == Font::ECompression::GRAYSCALE_UNCOMPRESSED )
    {
        size_t pixelCount = static_cast< size_t >( textureWidth ) * static_cast< size_t >( textureHeight );
        pOutputSheet->AddArray( pGrayscaleData, pixelCount );

        return;
    }

    // Convert the source image to a 32-bit BGRA image for the NVIDIA texture tools library to process.
    Image::InitParameters imageParameters;
    imageParameters.format.SetBytesPerPixel( 4 );
    imageParameters.format.SetChannelBitCount( Image::CHANNEL_RED, 8 );
    imageParameters.format.SetChannelBitCount( Image::CHANNEL_GREEN, 8 );
    imageParameters.format.SetChannelBitCount( Image::CHANNEL_BLUE, 8 );
    imageParameters.format.SetChannelBitCount( Image::CHANNEL_ALPHA, 8 );
#if HELIUM_ENDIAN_LITTLE
    imageParameters.format.SetChannelBitOffset( Image::CHANNEL_RED, 16 );
    imageParameters.format.SetChannelBitOffset( Image::CHANNEL_GREEN, 8 );
    imageParameters.format.SetChannelBitOffset( Image::CHANNEL_BLUE, 0 );
    imageParameters.format.SetChannelBitOffset( Image::CHANNEL_ALPHA, 24 );
#else
    imageParameters.format.SetChannelBitOffset( Image::CHANNEL_RED, 8 );
    imageParameters.format.SetChannelBitOffset( Image::CHANNEL_GREEN, 16 );
    imageParameters.format.SetChannelBitOffset( Image::CHANNEL_BLUE, 24 );
    imageParameters.format.SetChannelBitOffset( Image::CHANNEL_ALPHA, 0 );
#endif
    imageParameters.width = textureWidth;
    imageParameters.height = textureHeight;

    Image bgraImage;
    HELIUM_VERIFY( bgraImage.Initialize( imageParameters ) );

    uint_fast32_t imageWidth = textureWidth;
    uint_fast32_t imageHeight = textureHeight;
    uint_fast32_t imagePitch = bgraImage.GetPitch();

    uint8_t* pImagePixelData = static_cast< uint8_t* >( bgraImage.GetPixelData() );
    HELIUM_ASSERT( pImagePixelData );
    uint8_t* pImageRow = pImagePixelData;

    for( uint_fast32_t imageY = 0; imageY < imageHeight; ++imageY )
    {
        uint8_t* pOutputPixel = pImageRow;

        for( uint_fast32_t imageX = 0; imageX < imageWidth; ++imageX )
        {
            uint8_t pixel = *( pGrayscaleData++ );

            *( pOutputPixel++ ) = pixel;
            *( pOutputPixel++ ) = pixel;
            *( pOutputPixel++ ) = pixel;
            *( pOutputPixel++ ) = 0xff;
        }

        pImageRow += imagePitch;
    }

    // Set up the input options for the texture compressor.
    nvtt::InputOptions inputOptions;
    inputOptions.setTextureLayout( nvtt::TextureType_2D, textureWidth, textureHeight );
    inputOptions.setMipmapData( pImagePixelData, textureWidth, textureHeight );
    inputOptions.setMipmapGeneration( false );
    inputOptions.setWrapMode( nvtt::WrapMode_Repeat );
    inputOptions.setGamma( 1.0f, 1.0f );
    inputOptions.setNormalMap( false );

    // Set up the output options for the texture compressor.
    MemoryTextureOutputHandler outputHandler( textureWidth, textureHeight, false, false );

    nvtt::OutputOptions outputOptions;
    outputOptions.setOutputHandler( &outputHandler );
    outputOptions.setOutputHeader( false );

    // Set up the compression options for the texture compressor (note that the only compression option we currently
    // support other than uncompressed grayscale is BC1/DXT1).
    nvtt::CompressionOptions compressionOptions;
    compressionOptions.setFormat( nvtt::Format_BC1 );
    compressionOptions.setQuality( nvtt::Quality_Normal );

    // Compress the texture.
    nvtt::Compressor compressor;
    HELIUM_VERIFY( compressor.process( inputOptions, compressionOptions, outputOptions ) );

    // Store the compressed data in the output texture sheet.
    const MemoryTextureOutputHandler::MipLevelArray& rMipLevels = outputHandler.GetFace( 0 );
    HELIUM_ASSERT( rMipLevels.GetSize() == 1 );
    *pOutputSheet = rMipLevels[ 0 ];
}
Example #6
0
/// @copydoc ResourceHandler::CacheResource()
bool FontResourceHandler::CacheResource(
    ObjectPreprocessor* pObjectPreprocessor,
    Resource* pResource,
    const String& rSourceFilePath )
{
    HELIUM_ASSERT( pObjectPreprocessor );
    HELIUM_ASSERT( pResource );

    Font* pFont = Reflect::AssertCast< Font >( pResource );

    // Load the font into memory ourselves in order to make sure we properly support Unicode file names.
    FileStream* pFileStream = File::Open( rSourceFilePath, FileStream::MODE_READ );
    if( !pFileStream )
    {
        HELIUM_TRACE(
            TRACE_ERROR,
            TXT( "FontResourceHandler: Source file for font resource \"%s\" failed to open properly.\n" ),
            *rSourceFilePath );

        return false;
    }

    uint64_t fileSize64 = static_cast< uint64_t >( pFileStream->GetSize() );
    if( fileSize64 > SIZE_MAX )
    {
        HELIUM_TRACE(
            TRACE_ERROR,
            ( TXT( "FontResourceHandler: Font file \"%s\" exceeds the maximum addressable size of data in memory for " )
              TXT( "this platform and will not be cached.\n" ) ),
            *rSourceFilePath );

        delete pFileStream;

        return false;
    }

    size_t fileSize = static_cast< size_t >( fileSize64 );

    uint8_t* pFileData = new uint8_t [ fileSize ];
    if( !pFileData )
    {
        HELIUM_TRACE(
            TRACE_ERROR,
            ( TXT( "FontResourceHandler: Failed to allocate %" ) TPRIuSZ TXT( " bytes for resource data for font " )
              TXT( "\"%s\".\n" ) ),
            fileSize,
            *rSourceFilePath );

        delete pFileStream;

        return false;
    }

    size_t bytesRead = pFileStream->Read( pFileData, 1, fileSize );
    delete pFileStream;

    if( bytesRead != fileSize )
    {
        HELIUM_TRACE(
            TRACE_WARNING,
            ( TXT( "FontResourceHandler: Attempted to read %" ) TPRIuSZ TXT( " bytes from font resource file \"%s\", " )
              TXT( "but only %" ) TPRIuSZ TXT( " bytes were read successfully.\n" ) ),
            fileSize,
            *rSourceFilePath,
            bytesRead );
    }

    // Create the font face.
    FT_Library pLibrary = GetStaticLibrary();
    HELIUM_ASSERT( pLibrary );

    FT_Face pFace = NULL;
    FT_Error error = FT_New_Memory_Face( pLibrary, pFileData, static_cast< FT_Long >( bytesRead ), 0, &pFace );
    if( error != 0 )
    {
        HELIUM_TRACE(
            TRACE_ERROR,
            TXT( "FontResourceHandler: Failed to create font face from resource file \"%s\".\n" ),
            *rSourceFilePath );

        delete [] pFileData;

        return false;
    }

    // Set the appropriate font size.
    int32_t pointSize = Font::Float32ToFixed26x6( pFont->GetPointSize() );
    uint32_t dpi = pFont->GetDpi();

    error = FT_Set_Char_Size( pFace, pointSize, pointSize, dpi, dpi );
    if( error != 0 )
    {
        HELIUM_TRACE(
            TRACE_ERROR,
            TXT( "FontResourceHandler: Failed to set size of font resource \"%s\".\n" ),
            *rSourceFilePath );

        FT_Done_Face( pFace );
        delete [] pFileData;

        return false;
    }

    // Get the general font size information.
    FT_Size pSize = pFace->size;
    HELIUM_ASSERT( pSize );

    int32_t ascender = pSize->metrics.ascender;
    int32_t descender = pSize->metrics.descender;
    int32_t height = pSize->metrics.height;
    int32_t maxAdvance = pSize->metrics.max_advance;

    // Make sure that all characters in the font will fit on a single texture sheet (note that we also need at least a
    // pixel on each side in order to pad each glyph).
    uint16_t textureSheetWidth = Max< uint16_t >( pFont->GetTextureSheetWidth(), 1 );
    uint16_t textureSheetHeight = Max< uint16_t >( pFont->GetTextureSheetHeight(), 1 );

    int32_t integerHeight = ( height + ( 1 << 6 ) - 1 ) >> 6;
    if( integerHeight + 2 > textureSheetHeight )
    {
        HELIUM_TRACE(
            TRACE_ERROR,
            ( TXT( "FontResourceHandler: Font height (%" ) TPRId32 TXT( ") exceeds the texture sheet height (%" )
              TPRIu16 TXT( ") for font resource \"%s\".\n" ) ),
            integerHeight,
            textureSheetHeight,
            *pResource->GetPath().ToString() );

        FT_Done_Face( pFace );
        delete [] pFileData;

        return false;
    }

    int32_t integerMaxAdvance = ( maxAdvance + ( 1 << 6 ) - 1 ) >> 6;
    if( integerMaxAdvance + 2 > textureSheetWidth )
    {
        HELIUM_TRACE(
            TRACE_ERROR,
            ( TXT( "FontResourceHandler: Maximum character advance (%" ) TPRId32 TXT( ") exceeds the texture sheet " )
              TXT( "width (%" ) TPRIu16 TXT( ") for font resource \"%s\".\n" ) ),
            integerMaxAdvance,
            textureSheetWidth,
            *pResource->GetPath().ToString() );

        FT_Done_Face( pFace );
        delete [] pFileData;

        return false;
    }

    // Allocate a buffer for building our texture sheets.
    uint_fast32_t texturePixelCount =
        static_cast< uint_fast32_t >( textureSheetWidth ) * static_cast< uint_fast32_t >( textureSheetHeight );
    uint8_t* pTextureBuffer = new uint8_t [ texturePixelCount ];
    HELIUM_ASSERT( pTextureBuffer );
    if( !pTextureBuffer )
    {
        HELIUM_TRACE(
            TRACE_ERROR,
            ( TXT( "FontResourceHandler: Failed to allocate %" ) TPRIuFAST32 TXT( " bytes for texture resource " )
              TXT( "buffer data while caching font resource \"%s\".\n" ) ),
            texturePixelCount,
            *pResource->GetPath().ToString() );

        FT_Done_Face( pFace );
        delete [] pFileData;

        return false;
    }

    MemoryZero( pTextureBuffer, texturePixelCount );

    // Build the texture sheets for our glyphs.
    Font::ECompression textureCompression = pFont->GetTextureCompression();
    bool bAntialiased = pFont->GetAntialiased();

    DynArray< DynArray< uint8_t > > textureSheets;
    DynArray< Font::Character > characters;

    uint16_t penX = 1;
    uint16_t penY = 1;
    uint16_t lineHeight = 0;

    FT_Int32 glyphLoadFlags = FT_LOAD_RENDER;
    if( !bAntialiased )
    {
        glyphLoadFlags |= FT_LOAD_TARGET_MONO;
    }

    for( uint_fast32_t codePoint = 0; codePoint <= UNICODE_CODE_POINT_MAX; ++codePoint )
    {
        // Check whether the current code point is contained within the font.
        FT_UInt characterIndex = FT_Get_Char_Index( pFace, static_cast< FT_ULong >( codePoint ) );
        if( characterIndex == 0 )
        {
            continue;
        }

        // Load and render the glyph for the current character.
        HELIUM_VERIFY( FT_Load_Glyph( pFace, characterIndex, glyphLoadFlags ) == 0 );

        FT_GlyphSlot pGlyph = pFace->glyph;
        HELIUM_ASSERT( pGlyph );

        // Proceed to the next line in the texture sheet or the next sheet itself if we don't have enough room in the
        // current line/sheet.
        HELIUM_ASSERT( pGlyph->bitmap.rows >= 0 );
        HELIUM_ASSERT( pGlyph->bitmap.width >= 0 );
        uint_fast32_t glyphRowCount = static_cast< uint32_t >( pGlyph->bitmap.rows );
        uint_fast32_t glyphWidth = static_cast< uint32_t >( pGlyph->bitmap.width );

        if( penX + glyphWidth + 1 >= textureSheetWidth )
        {
            penX = 1;

            if( penY + glyphRowCount + 1 >= textureSheetHeight )
            {
                CompressTexture(
                    pTextureBuffer,
                    textureSheetWidth,
                    textureSheetHeight,
                    textureCompression,
                    textureSheets );
                MemoryZero( pTextureBuffer, texturePixelCount );

                penY = 1;
            }
            else
            {
                penY += lineHeight + 1;
            }

            lineHeight = 0;
        }

        // Copy the character data from the glyph bitmap to the texture sheet.
        int_fast32_t glyphPitch = pGlyph->bitmap.pitch;

        const uint8_t* pGlyphBuffer = pGlyph->bitmap.buffer;
        HELIUM_ASSERT( pGlyphBuffer || glyphRowCount == 0 );

        uint8_t* pTexturePixel =
            pTextureBuffer + static_cast< size_t >( penY ) * static_cast< size_t >( textureSheetWidth ) + penX;

        if( bAntialiased )
        {
            // Anti-aliased fonts are rendered as 8-bit grayscale images, so just copy the data as-is.
            for( uint_fast32_t rowIndex = 0; rowIndex < glyphRowCount; ++rowIndex )
            {
                MemoryCopy( pTexturePixel, pGlyphBuffer, glyphWidth );
                pGlyphBuffer += glyphPitch;
                pTexturePixel += textureSheetWidth;
            }
        }
        else
        {
            // Fonts without anti-aliasing are rendered as 1-bit monochrome images, so we need to manually convert each
            // row to 8-bit grayscale.
            for( uint_fast32_t rowIndex = 0; rowIndex < glyphRowCount; ++rowIndex )
            {
                const uint8_t* pGlyphPixelBlock = pGlyphBuffer;
                pGlyphBuffer += glyphPitch;

                uint8_t* pCurrentTexturePixel = pTexturePixel;
                pTexturePixel += textureSheetWidth;

                uint_fast32_t remainingPixelCount = glyphWidth;
                while( remainingPixelCount >= 8 )
                {
                    remainingPixelCount -= 8;

                    uint8_t pixelBlock = *pGlyphPixelBlock;
                    ++pGlyphPixelBlock;

                    *( pCurrentTexturePixel++ ) = ( ( pixelBlock & ( 1 << 7 ) ) ? 255 : 0 );
                    *( pCurrentTexturePixel++ ) = ( ( pixelBlock & ( 1 << 6 ) ) ? 255 : 0 );
                    *( pCurrentTexturePixel++ ) = ( ( pixelBlock & ( 1 << 5 ) ) ? 255 : 0 );
                    *( pCurrentTexturePixel++ ) = ( ( pixelBlock & ( 1 << 4 ) ) ? 255 : 0 );
                    *( pCurrentTexturePixel++ ) = ( ( pixelBlock & ( 1 << 3 ) ) ? 255 : 0 );
                    *( pCurrentTexturePixel++ ) = ( ( pixelBlock & ( 1 << 2 ) ) ? 255 : 0 );
                    *( pCurrentTexturePixel++ ) = ( ( pixelBlock & ( 1 << 1 ) ) ? 255 : 0 );
                    *( pCurrentTexturePixel++ ) = ( ( pixelBlock & ( 1 << 0 ) ) ? 255 : 0 );
                }

                uint8_t pixelBlock = *pGlyphPixelBlock;
                uint8_t mask = ( 1 << 7 );
                while( remainingPixelCount != 0 )
                {
                    *( pCurrentTexturePixel++ ) = ( ( pixelBlock & mask ) ? 255 : 0 );
                    mask >>= 1;
                    --remainingPixelCount;
                }
            }
        }

        // Store the character information in our character array.
        Font::Character* pCharacter = characters.New();
        HELIUM_ASSERT( pCharacter );
        pCharacter->codePoint = static_cast< uint32_t >( codePoint );

        pCharacter->imageX = penX;
        pCharacter->imageY = penY;
        pCharacter->imageWidth = static_cast< uint16_t >( glyphWidth );
        pCharacter->imageHeight = static_cast< uint16_t >( glyphRowCount );

        pCharacter->width = pGlyph->metrics.width;
        pCharacter->height = pGlyph->metrics.height;
        pCharacter->bearingX = pGlyph->metrics.horiBearingX;
        pCharacter->bearingY = pGlyph->metrics.horiBearingY;
        pCharacter->advance = pGlyph->metrics.horiAdvance;

        HELIUM_ASSERT( textureSheets.GetSize() < UINT8_MAX );
        pCharacter->texture = static_cast< uint8_t >( static_cast< uint8_t >( textureSheets.GetSize() ) );

        // Update the pen location as well as the maximum line height as appropriate based on the current line height.
        penX += static_cast< uint16_t >( glyphWidth ) + 1;

        HELIUM_ASSERT( glyphRowCount <= UINT16_MAX );
        lineHeight = Max< uint16_t >( lineHeight, static_cast< uint16_t >( glyphRowCount ) );
    }

    // Compress and store the last texture in the sheet.
    if( !characters.IsEmpty() )
    {
        CompressTexture( pTextureBuffer, textureSheetWidth, textureSheetHeight, textureCompression, textureSheets );
    }

    // Done processing the font itself, so free some resources.
    delete [] pTextureBuffer;

    FT_Done_Face( pFace );
    delete [] pFileData;

    // Cache the font data.
    size_t characterCountActual = characters.GetSize();
    HELIUM_ASSERT( characterCountActual <= UINT32_MAX );
    uint32_t characterCount = static_cast< uint32_t >( characterCountActual );

    size_t textureCountActual = textureSheets.GetSize();
    HELIUM_ASSERT( textureCountActual < UINT8_MAX );
    uint8_t textureCount = static_cast< uint8_t >( textureCountActual );

    BinarySerializer persistentDataSerializer;
    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;
        }

        persistentDataSerializer.SetByteSwapping( pPreprocessor->SwapBytes() );
        persistentDataSerializer.BeginSerialize();

        persistentDataSerializer << ascender;
        persistentDataSerializer << descender;
        persistentDataSerializer << height;
        persistentDataSerializer << maxAdvance;
        persistentDataSerializer << characterCount;
        persistentDataSerializer << textureCount;

        for( size_t characterIndex = 0; characterIndex < characterCountActual; ++characterIndex )
        {
            characters[ characterIndex ].Serialize( persistentDataSerializer );
        }

        persistentDataSerializer.EndSerialize();

        Resource::PreprocessedData& rPreprocessedData = pResource->GetPreprocessedData(
            static_cast< Cache::EPlatform >( platformIndex ) );
        rPreprocessedData.persistentDataBuffer = persistentDataSerializer.GetPropertyStreamBuffer();
        rPreprocessedData.subDataBuffers = textureSheets;
        rPreprocessedData.bLoaded = true;
    }

    return true;
}
Example #7
0
/// @copydoc ResourceHandler::CacheResource()
bool ShaderVariantResourceHandler::CacheResource(
    ObjectPreprocessor* pObjectPreprocessor,
    Resource* pResource,
    const String& rSourceFilePath )
{
    HELIUM_ASSERT( pObjectPreprocessor );
    HELIUM_ASSERT( pResource );

    ShaderVariant* pVariant = Reflect::AssertCast< ShaderVariant >( pResource );

    // Parse the shader type and user option index from the variant name.
    Name variantName = pVariant->GetName();
    const tchar_t* pVariantNameString = *variantName;
    HELIUM_ASSERT( pVariantNameString );

    tchar_t shaderTypeCharacter = pVariantNameString[ 0 ];
    HELIUM_ASSERT( shaderTypeCharacter != TXT( '\0' ) );

    RShader::EType shaderType;
    switch( shaderTypeCharacter )
    {
    case TXT( 'v' ):
    {
        shaderType = RShader::TYPE_VERTEX;
        break;
    }

    case TXT( 'p' ):
    {
        shaderType = RShader::TYPE_PIXEL;
        break;
    }

    default:
    {
        HELIUM_TRACE(
            TRACE_ERROR,
            ( TXT( "ShaderVariantResourceHandler: Failed to determine shader type from the name of object " )
              TXT( "\"%s\".\n" ) ),
            *pVariant->GetPath().ToString() );

        return false;
    }
    }

    uint32_t userOptionIndex = 0;
    ++pVariantNameString;
    int parseResult;
#if HELIUM_UNICODE
#if HELIUM_CC_CL
    parseResult = swscanf_s( pVariantNameString, TXT( "%" ) TSCNu32, &userOptionIndex );
#else
    parseResult = swscanf( pVariantNameString, TXT( "%" ) TSCNu32, &userOptionIndex );
#endif
#else
#if HELIUM_CC_CL
    parseResult = sscanf_s( pVariantNameString, TXT( "%" ) TSCNu32, &userOptionIndex );
#else
    parseResult = sscanf( pVariantNameString, TXT( "%" ) TSCNu32, &userOptionIndex );
#endif
#endif
    if( parseResult != 1 )
    {
        HELIUM_TRACE(
            TRACE_ERROR,
            ( TXT( "ShaderVariantResourceHandler: Failed to parse user shader option set index from the name of " )
              TXT( "option \"%s\".\n" ) ),
            *pVariant->GetPath().ToString() );

        return false;
    }

    // Get the parent shader.
    Shader* pShader = Reflect::AssertCast< Shader >( pVariant->GetOwner() );
    HELIUM_ASSERT( pShader );
    HELIUM_ASSERT( pShader->GetAnyFlagSet( GameObject::FLAG_PRECACHED ) );

    // Acquire the user preprocessor option set associated with the target shader type and user option set index.
    const Shader::Options& rUserOptions = pShader->GetUserOptions();

    DynArray< Name > toggleNames;
    DynArray< Shader::SelectPair > selectPairs;
    rUserOptions.GetOptionSetFromIndex( shaderType, userOptionIndex, toggleNames, selectPairs );

    DynArray< PlatformPreprocessor::ShaderToken > shaderTokens;

    size_t userToggleNameCount = toggleNames.GetSize();
    for( size_t toggleNameIndex = 0; toggleNameIndex < userToggleNameCount; ++toggleNameIndex )
    {
        PlatformPreprocessor::ShaderToken* pToken = shaderTokens.New();
        HELIUM_ASSERT( pToken );
        StringConverter< tchar_t, char >::Convert( pToken->name, *toggleNames[ toggleNameIndex ] );
        pToken->definition = "1";
    }

    size_t userSelectPairCount = selectPairs.GetSize();
    for( size_t selectPairIndex = 0; selectPairIndex < userSelectPairCount; ++selectPairIndex )
    {
        const Shader::SelectPair& rPair = selectPairs[ selectPairIndex ];

        PlatformPreprocessor::ShaderToken* pToken = shaderTokens.New();
        HELIUM_ASSERT( pToken );
        StringConverter< tchar_t, char >::Convert( pToken->name, *rPair.name );
        pToken->definition = "1";

        pToken = shaderTokens.New();
        HELIUM_ASSERT( pToken );
        StringConverter< tchar_t, char >::Convert( pToken->name, *rPair.choice );
        pToken->definition = "1";
    }

    size_t userShaderTokenCount = shaderTokens.GetSize();

    // Load the entire shader resource into memory.
    FileStream* pSourceFileStream = File::Open( rSourceFilePath, FileStream::MODE_READ );
    if( !pSourceFileStream )
    {
        HELIUM_TRACE(
            TRACE_ERROR,
            ( TXT( "ShaderVariantResourceHandler: Source file for shader variant resource \"%s\" failed to open " )
              TXT( "properly.\n" ) ),
            *pVariant->GetPath().ToString() );

        return false;
    }

    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(
            TRACE_ERROR,
            ( TXT( "ShaderVariantResourceHandler: Source file for shader resource \"%s\" is too large to fit " )
              TXT( "into memory for preprocessing.\n" ) ),
            *pShader->GetPath().ToString() );

        delete pSourceFileStream;

        return false;
    }

    size_t size = static_cast< size_t >( size64 );

    DefaultAllocator allocator;
    void* pShaderSource = allocator.Allocate( size );
    HELIUM_ASSERT( pShaderSource );
    if( !pShaderSource )
    {
        HELIUM_TRACE(
            TRACE_ERROR,
            ( TXT( "ShaderVariantResourceHandler: Failed to allocate %" ) TPRIuSZ TXT( " bytes for loading the " )
              TXT( "source data of \"%s\" for preprocessing.\n" ) ),
            size,
            *pShader->GetPath().ToString() );

        delete pSourceFileStream;

        return false;
    }

    BufferedStream( pSourceFileStream ).Read( pShaderSource, 1, size );

    delete pSourceFileStream;

    // Compile each variant of system options for each shader profile in each supported target platform.
    const Shader::Options& rSystemOptions = pShader->GetSystemOptions();
    size_t systemOptionSetCount = rSystemOptions.ComputeOptionSetCount( shaderType );
    if( systemOptionSetCount > UINT32_MAX )
    {
        HELIUM_TRACE(
            TRACE_ERROR,
            ( TXT( "ShaderVariantResourceHandler: System option set count (%" ) TPRIuSZ TXT( ") in shader \"%s\" " )
              TXT( "exceeds the maximum supported (%" ) TPRIuSZ TXT( ").\n" ) ),
            systemOptionSetCount,
            *pShader->GetPath().ToString(),
            static_cast< size_t >( UINT32_MAX ) );

        allocator.Free( pShaderSource );

        return false;
    }

    uint32_t systemOptionSetCount32 = static_cast< uint32_t >( systemOptionSetCount );

    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 = pVariant->GetPreprocessedData(
                    static_cast< Cache::EPlatform >( platformIndex ) );

        ShaderVariant::PersistentResourceData persistentResourceData;
        persistentResourceData.m_resourceCount = systemOptionSetCount32;
        SaveObjectToPersistentDataBuffer(&persistentResourceData, rPreprocessedData.persistentDataBuffer);

        size_t shaderProfileCount = pPreprocessor->GetShaderProfileCount();
        size_t shaderCount = shaderProfileCount * systemOptionSetCount;

        DynArray< DynArray< uint8_t > >& rSubDataBuffers = rPreprocessedData.subDataBuffers;
        rSubDataBuffers.Reserve( shaderCount );
        rSubDataBuffers.Resize( 0 );
        rSubDataBuffers.Resize( shaderCount );
        rSubDataBuffers.Trim();

        rPreprocessedData.bLoaded = true;
    }

//     DynArray< uint8_t > compiledCodeBuffer;
//     DynArray< ShaderConstantBufferInfo > constantBuffers, pcSm4ConstantBuffers;
//     DynArray< ShaderSamplerInfo > samplerInputs;
//     DynArray< ShaderTextureInfo > textureInputs;

    CompiledShaderData csd_pc_sm4;

    for( size_t systemOptionSetIndex = 0; systemOptionSetIndex < systemOptionSetCount; ++systemOptionSetIndex )
    {
        rSystemOptions.GetOptionSetFromIndex( shaderType, systemOptionSetIndex, toggleNames, selectPairs );

        size_t systemToggleNameCount = toggleNames.GetSize();
        for( size_t toggleNameIndex = 0; toggleNameIndex < systemToggleNameCount; ++toggleNameIndex )
        {
            PlatformPreprocessor::ShaderToken* pToken = shaderTokens.New();
            HELIUM_ASSERT( pToken );
            StringConverter< tchar_t, char >::Convert( pToken->name, *toggleNames[ toggleNameIndex ] );
            pToken->definition = "1";
        }

        size_t systemSelectPairCount = selectPairs.GetSize();
        for( size_t selectPairIndex = 0; selectPairIndex < systemSelectPairCount; ++selectPairIndex )
        {
            const Shader::SelectPair& rPair = selectPairs[ selectPairIndex ];

            PlatformPreprocessor::ShaderToken* pToken = shaderTokens.New();
            HELIUM_ASSERT( pToken );
            StringConverter< tchar_t, char >::Convert( pToken->name, *rPair.name );
            pToken->definition = "1";

            pToken = shaderTokens.New();
            HELIUM_ASSERT( pToken );
            StringConverter< tchar_t, char >::Convert( pToken->name, *rPair.choice );
            pToken->definition = "1";
        }

        // Compile for PC shader model 4 first so that we can get the constant buffer information.
        PlatformPreprocessor* pPreprocessor = pObjectPreprocessor->GetPlatformPreprocessor( Cache::PLATFORM_PC );
        HELIUM_ASSERT( pPreprocessor );

        csd_pc_sm4.compiledCodeBuffer.Resize( 0 );
        bool bCompiled = CompileShader(
                             pVariant,
                             pPreprocessor,
                             Cache::PLATFORM_PC,
                             ShaderProfile::PC_SM4,
                             shaderType,
                             pShaderSource,
                             size,
                             shaderTokens,
                             csd_pc_sm4.compiledCodeBuffer );
        if( !bCompiled )
        {
            HELIUM_TRACE(
                TRACE_ERROR,
                ( TXT( "ShaderVariantResourceHandler: Failed to compile shader for PC shader model 4, which is " )
                  TXT( "needed for reflection purposes.  Additional shader targets will not be built.\n" ) ) );
        }
        else
        {
            csd_pc_sm4.constantBuffers.Resize( 0 );
            csd_pc_sm4.samplerInputs.Resize( 0 );
            csd_pc_sm4.textureInputs.Resize( 0 );
            bool bReadConstantBuffers = pPreprocessor->FillShaderReflectionData(
                                            ShaderProfile::PC_SM4,
                                            csd_pc_sm4.compiledCodeBuffer.GetData(),
                                            csd_pc_sm4.compiledCodeBuffer.GetSize(),
                                            csd_pc_sm4.constantBuffers,
                                            csd_pc_sm4.samplerInputs,
                                            csd_pc_sm4.textureInputs );
            if( !bReadConstantBuffers )
            {
                HELIUM_TRACE(
                    TRACE_ERROR,
                    ( TXT( "ShaderVariantResourceHandler: Failed to read reflection information for PC shader " )
                      TXT( "model 4.  Additional shader targets will not be built.\n" ) ) );
            }
            else
            {
                Resource::PreprocessedData& rPcPreprocessedData = pVariant->GetPreprocessedData(
                            Cache::PLATFORM_PC );
                DynArray< DynArray< uint8_t > >& rPcSubDataBuffers = rPcPreprocessedData.subDataBuffers;
                DynArray< uint8_t >& rPcSm4SubDataBuffer =
                    rPcSubDataBuffers[ ShaderProfile::PC_SM4 * systemOptionSetCount + systemOptionSetIndex ];

                Cache::WriteCacheObjectToBuffer(csd_pc_sm4, rPcSm4SubDataBuffer);

                // 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;
                    }

                    // GET PLATFORM'S SUBDATA BUFFER
                    Resource::PreprocessedData& rPreprocessedData = pVariant->GetPreprocessedData(
                                static_cast< Cache::EPlatform >( platformIndex ) );
                    DynArray< DynArray< uint8_t > >& rSubDataBuffers = rPreprocessedData.subDataBuffers;

                    size_t shaderProfileCount = pPreprocessor->GetShaderProfileCount();
                    for( size_t shaderProfileIndex = 0;
                            shaderProfileIndex < shaderProfileCount;
                            ++shaderProfileIndex )
                    {
                        CompiledShaderData csd;

                        // Already cached PC shader model 4...
                        if( shaderProfileIndex == ShaderProfile::PC_SM4 && platformIndex == Cache::PLATFORM_PC )
                        {
                            continue;
                        }

                        bCompiled = CompileShader(
                                        pVariant,
                                        pPreprocessor,
                                        platformIndex,
                                        shaderProfileIndex,
                                        shaderType,
                                        pShaderSource,
                                        size,
                                        shaderTokens,
                                        csd.compiledCodeBuffer );
                        if( !bCompiled )
                        {
                            continue;
                        }

                        csd.constantBuffers = csd_pc_sm4.constantBuffers;
                        csd.samplerInputs.Resize( 0 );
                        csd.textureInputs.Resize( 0 );
                        bReadConstantBuffers = pPreprocessor->FillShaderReflectionData(
                                                   shaderProfileIndex,
                                                   csd.compiledCodeBuffer.GetData(),
                                                   csd.compiledCodeBuffer.GetSize(),
                                                   csd.constantBuffers,
                                                   csd.samplerInputs,
                                                   csd.textureInputs );
                        if( !bReadConstantBuffers )
                        {
                            continue;
                        }

                        DynArray< uint8_t >& rTargetSubDataBuffer =
                            rSubDataBuffers[ shaderProfileIndex * systemOptionSetCount + systemOptionSetIndex ];
                        Cache::WriteCacheObjectToBuffer(csd, rTargetSubDataBuffer);
                    }
                }
            }
        }

        // Trim the system tokens off the shader token list for the next pass.
        shaderTokens.Resize( userShaderTokenCount );
    }

    allocator.Free( pShaderSource );

    return true;
}
Example #8
0
/// Synchronize the shader parameter list with those provided by the selected shader variant.
///
/// @see SynchronizeFloatVectorParameters(), SynchronizeTextureParameters()
void Material::SynchronizeShaderParameters()
{
    Shader* pShader = m_spShader;
    if( !pShader )
    {
        m_float1Parameters.Clear();
        m_float2Parameters.Clear();
        m_float3Parameters.Clear();
        m_float4Parameters.Clear();
        m_textureParameters.Clear();
    }

    // Synchronize floating-point constant parameters.
    Name parameterConstantBufferName = GetParameterConstantBufferName();

    size_t existingFloat1Count = m_float1Parameters.GetSize();
    size_t existingFloat2Count = m_float2Parameters.GetSize();
    size_t existingFloat3Count = m_float3Parameters.GetSize();
    size_t existingFloat4Count = m_float4Parameters.GetSize();

    DynArray< Float1Parameter > newFloat1Parameters;
    DynArray< Float2Parameter > newFloat2Parameters;
    DynArray< Float3Parameter > newFloat3Parameters;
    DynArray< Float4Parameter > newFloat4Parameters;
    for( size_t shaderTypeIndex = 0; shaderTypeIndex < HELIUM_ARRAY_COUNT( m_shaderVariants ); ++shaderTypeIndex )
    {
        ShaderVariant* pShaderVariant = m_shaderVariants[ shaderTypeIndex ];
        if( !pShaderVariant )
        {
            continue;
        }

        const ShaderConstantBufferInfoSet* pBufferSet = pShaderVariant->GetConstantBufferInfoSet( 0 );
        if( !pBufferSet )
        {
            continue;
        }

        bool bCheckDuplicates =
            ( !newFloat1Parameters.IsEmpty() || !newFloat2Parameters.IsEmpty() || !newFloat3Parameters.IsEmpty() ||
            !newFloat4Parameters.IsEmpty() );

        const DynArray< ShaderConstantBufferInfo >& rBuffers = pBufferSet->buffers;
        size_t bufferCount = rBuffers.GetSize();
        for( size_t bufferIndex = 0; bufferIndex < bufferCount; ++bufferIndex )
        {
            const ShaderConstantBufferInfo& rBufferInfo = rBuffers[ bufferIndex ];
            if( rBufferInfo.name != parameterConstantBufferName )
            {
                continue;
            }

            const DynArray< ShaderConstantInfo >& rConstants = rBufferInfo.constants;
            size_t constantCount = rConstants.GetSize();
            for( size_t constantIndex = 0; constantIndex < constantCount; ++constantIndex )
            {
                const ShaderConstantInfo& rConstantInfo = rConstants[ constantIndex ];

                // Constants must be between 1 and 4 floating-point values.
                uint16_t constantSize = rConstantInfo.usedSize;
                if( constantSize < sizeof( float32_t ) || constantSize > sizeof( float32_t ) * 4 )
                {
                    continue;
                }

                Name constantName = rConstantInfo.name;

                size_t parameterIndex;
                if( bCheckDuplicates )
                {
                    size_t parameterCount = newFloat1Parameters.GetSize();
                    for( parameterIndex = 0; parameterIndex < parameterCount; ++parameterIndex )
                    {
                        if( newFloat1Parameters[ parameterIndex ].name == constantName )
                        {
                            break;
                        }
                    }

                    if( parameterIndex < parameterCount )
                    {
                        continue;
                    }

                    parameterCount = newFloat2Parameters.GetSize();
                    for( parameterIndex = 0; parameterIndex < parameterCount; ++parameterIndex )
                    {
                        if( newFloat2Parameters[ parameterIndex ].name == constantName )
                        {
                            break;
                        }
                    }

                    if( parameterIndex < parameterCount )
                    {
                        continue;
                    }

                    parameterCount = newFloat3Parameters.GetSize();
                    for( parameterIndex = 0; parameterIndex < parameterCount; ++parameterIndex )
                    {
                        if( newFloat3Parameters[ parameterIndex ].name == constantName )
                        {
                            break;
                        }
                    }

                    if( parameterIndex < parameterCount )
                    {
                        continue;
                    }

                    parameterCount = newFloat4Parameters.GetSize();
                    for( parameterIndex = 0; parameterIndex < parameterCount; ++parameterIndex )
                    {
                        if( newFloat4Parameters[ parameterIndex ].name == constantName )
                        {
                            break;
                        }
                    }

                    if( parameterIndex < parameterCount )
                    {
                        continue;
                    }
                }

                Simd::Vector4 newValue( 0.0f );
                for( parameterIndex = 0; parameterIndex < existingFloat1Count; ++parameterIndex )
                {
                    const Float1Parameter& rExistingParameter = m_float1Parameters[ parameterIndex ];
                    if( rExistingParameter.name == constantName )
                    {
                        newValue.SetElement( 0, rExistingParameter.value );

                        break;
                    }
                }

                if( parameterIndex >= existingFloat1Count )
                {
                    for( parameterIndex = 0; parameterIndex < existingFloat2Count; ++parameterIndex )
                    {
                        const Float2Parameter& rExistingParameter = m_float2Parameters[ parameterIndex ];
                        if( rExistingParameter.name == constantName )
                        {
                            newValue.SetElement( 0, rExistingParameter.value.GetX() );
                            newValue.SetElement( 1, rExistingParameter.value.GetY() );

                            break;
                        }
                    }

                    if( parameterIndex >= existingFloat2Count )
                    {
                        for( parameterIndex = 0; parameterIndex < existingFloat3Count; ++parameterIndex )
                        {
                            const Float3Parameter& rExistingParameter = m_float3Parameters[ parameterIndex ];
                            if( rExistingParameter.name == constantName )
                            {
                                newValue.SetElement( 0, rExistingParameter.value.GetElement( 0 ) );
                                newValue.SetElement( 1, rExistingParameter.value.GetElement( 1 ) );
                                newValue.SetElement( 2, rExistingParameter.value.GetElement( 2 ) );

                                break;
                            }
                        }

                        if( parameterIndex >= existingFloat3Count )
                        {
                            for( parameterIndex = 0; parameterIndex < existingFloat4Count; ++parameterIndex )
                            {
                                const Float4Parameter& rExistingParameter =
                                    m_float4Parameters[ parameterIndex ];
                                if( rExistingParameter.name == constantName )
                                {
                                    newValue = rExistingParameter.value;

                                    break;
                                }
                            }
                        }
                    }
                }

                if( constantSize < sizeof( float32_t ) * 2 )
                {
                    Float1Parameter* pParameter = newFloat1Parameters.New();
                    HELIUM_ASSERT( pParameter );
                    pParameter->name = constantName;
                    pParameter->value = newValue.GetElement( 0 );
                }
                else if( constantSize < sizeof( float32_t ) * 3 )
                {
                    Float2Parameter* pParameter = newFloat2Parameters.New();
                    HELIUM_ASSERT( pParameter );
                    pParameter->name = constantName;
                    pParameter->value = Simd::Vector2( newValue.GetElement( 0 ), newValue.GetElement( 1 ) );
                }
                else if( constantSize < sizeof( float32_t ) * 4 )
                {
                    Float3Parameter* pParameter = newFloat3Parameters.New();
                    HELIUM_ASSERT( pParameter );
                    pParameter->name = constantName;
                    pParameter->value =
                        Simd::Vector3( newValue.GetElement( 0 ), newValue.GetElement( 1 ), newValue.GetElement( 2 ) );
                }
                else
                {
                    Float4Parameter* pParameter = newFloat4Parameters.New();
                    HELIUM_ASSERT( pParameter );
                    pParameter->name = constantName;
                    pParameter->value = newValue;
                }
            }
        }
    }

    newFloat1Parameters.Trim();
    newFloat2Parameters.Trim();
    newFloat3Parameters.Trim();
    newFloat4Parameters.Trim();
    m_float1Parameters.Swap( newFloat1Parameters );
    m_float2Parameters.Swap( newFloat2Parameters );
    m_float3Parameters.Swap( newFloat3Parameters );
    m_float4Parameters.Swap( newFloat4Parameters );
    newFloat1Parameters.Clear();
    newFloat2Parameters.Clear();
    newFloat3Parameters.Clear();
    newFloat4Parameters.Clear();

    // Synchronize texture parameters.
    size_t existingTextureCount = m_textureParameters.GetSize();

    DynArray< TextureParameter > newTextureParameters;
    for( size_t shaderTypeIndex = 0; shaderTypeIndex < HELIUM_ARRAY_COUNT( m_shaderVariants ); ++shaderTypeIndex )
    {
        ShaderVariant* pShaderVariant = m_shaderVariants[ shaderTypeIndex ];
        if( !pShaderVariant )
        {
            continue;
        }

        const ShaderTextureInfoSet* pTextureSet = pShaderVariant->GetTextureInfoSet( 0 );
        if( !pTextureSet )
        {
            continue;
        }

        bool bCheckDuplicates = !newTextureParameters.IsEmpty();

        const DynArray< ShaderTextureInfo >& rTextureInputs = pTextureSet->inputs;
        size_t textureInputCount = rTextureInputs.GetSize();
        for( size_t textureIndex = 0; textureIndex < textureInputCount; ++textureIndex )
        {
            const ShaderTextureInfo& rTextureInfo = rTextureInputs[ textureIndex ];

            // Ignore textures prefixed with an underscore, as they are reserved for system use.
            Name textureInputName = rTextureInfo.name;
            if( !textureInputName.IsEmpty() && ( *textureInputName )[ 0 ] == TXT( '_' ) )
            {
                continue;
            }

            size_t parameterIndex;
            if( bCheckDuplicates )
            {
                size_t textureParameterCount = newTextureParameters.GetSize();
                for( parameterIndex = 0; parameterIndex < textureParameterCount; ++parameterIndex )
                {
                    if( newTextureParameters[ parameterIndex ].name == textureInputName )
                    {
                        break;
                    }
                }

                if( parameterIndex < textureParameterCount )
                {
                    continue;
                }
            }

            TextureParameter* pParameter = newTextureParameters.New();
            HELIUM_ASSERT( pParameter );
            pParameter->name = textureInputName;

            for( parameterIndex = 0; parameterIndex < existingTextureCount; ++parameterIndex )
            {
                const TextureParameter& rTextureParameter = m_textureParameters[ parameterIndex ];
                if( rTextureParameter.name == textureInputName )
                {
                    pParameter->value = rTextureParameter.value;

                    break;
                }
            }
        }
    }

    newTextureParameters.Trim();
    m_textureParameters.Swap( newTextureParameters );
    newTextureParameters.Clear();
}