Example #1
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 #2
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();
}