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 } } }
/// 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 ]; } } }
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 } }
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 ) ) ); } }
/// 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 ]; }
/// @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; }
/// @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; }
/// 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(); }