/** * Copies bulk data from passed in structure. * * @param Other Bulk data object to copy from. */ void FUntypedBulkData::Copy( const FUntypedBulkData& Other ) { // Only copy if there is something to copy. if( Other.GetElementCount() ) { // Make sure src is loaded without calling Lock as the object is const. check(Other.BulkData); check(BulkData); check(ElementCount == Other.GetElementCount() ); // Copy from src to dest. FMemory::Memcpy( BulkData, Other.BulkData, Other.GetBulkDataSize() ); } }
/** * Copy constructor. Use the common routine to perform the copy. * * @param Other the source array to copy */ FUntypedBulkData::FUntypedBulkData( const FUntypedBulkData& Other ) { InitializeMemberVariables(); // Prepare bulk data pointer. Can't call any functions that would call virtual GetElementSize on "this" as // we're in the constructor of the base class and would hence call a pure virtual. ElementCount = Other.ElementCount; check(bShouldFreeOnEmpty); BulkData = FMemory::Realloc( BulkData, Other.GetBulkDataSize() ); // Copy data over. Copy( Other ); #if TRACK_BULKDATA_USE FThreadSafeBulkDataToObjectMap::Get().Add( this, NULL ); #endif }
bool UFactory::ImportUntypedBulkDataFromText(const TCHAR*& Buffer, FUntypedBulkData& BulkData) { FString StrLine; int32 ElementCount = 0; int32 ElementSize = 0; bool bBulkDataIsLocked = false; while(FParse::Line(&Buffer,StrLine)) { FString ParsedText; const TCHAR* Str = *StrLine; if (FParse::Value(Str, TEXT("ELEMENTCOUNT="), ParsedText)) { /** Number of elements in bulk data array */ ElementCount = FCString::Atoi(*ParsedText); } else if (FParse::Value(Str, TEXT("ELEMENTSIZE="), ParsedText)) { /** Serialized flags for bulk data */ ElementSize = FCString::Atoi(*ParsedText); } else if (FParse::Value(Str, TEXT("BEGIN "), ParsedText) && (ParsedText.ToUpper() == TEXT("BINARYBLOB"))) { uint8* RawData = NULL; /** The bulk data... */ while(FParse::Line(&Buffer,StrLine)) { Str = *StrLine; if (FParse::Value(Str, TEXT("SIZE="), ParsedText)) { int32 Size = FCString::Atoi(*ParsedText); check(Size == (ElementSize *ElementCount)); BulkData.Lock(LOCK_READ_WRITE); void* RawBulkData = BulkData.Realloc(ElementCount); RawData = (uint8*)RawBulkData; bBulkDataIsLocked = true; } else if (FParse::Value(Str, TEXT("BEGIN "), ParsedText) && (ParsedText.ToUpper() == TEXT("BINARY"))) { uint8* BulkDataPointer = RawData; while(FParse::Line(&Buffer,StrLine)) { Str = *StrLine; TCHAR* ParseStr = (TCHAR*)(Str); if (FParse::Value(Str, TEXT("END "), ParsedText) && (ParsedText.ToUpper() == TEXT("BINARY"))) { break; } // Clear whitespace while ((*ParseStr == L' ') || (*ParseStr == L'\t')) { ParseStr++; } // Parse the values into the bulk data... while ((*ParseStr != L'\n') && (*ParseStr != L'\r') && (*ParseStr != 0)) { int32 Value; if (!FCString::Strnicmp(ParseStr, TEXT("0x"), 2)) { ParseStr +=2; } Value = FParse::HexDigit(ParseStr[0]) * 16 + FParse::HexDigit(ParseStr[1]); *BulkDataPointer = (uint8)Value; BulkDataPointer++; ParseStr += 2; ParseStr++; } } } else if (FParse::Value(Str, TEXT("END "), ParsedText) && (ParsedText.ToUpper() == TEXT("BINARYBLOB"))) { BulkData.Unlock(); bBulkDataIsLocked = false; break; } } } else if (FParse::Value(Str, TEXT("END "), ParsedText) && (ParsedText.ToUpper() == TEXT("UNTYPEDBULKDATA"))) { break; } } if (bBulkDataIsLocked == true) { BulkData.Unlock(); } return true; }
/** * Dumps detailed information of bulk data usage. * * @param Log FOutputDevice to use for logging */ void FUntypedBulkData::DumpBulkDataUsage( FOutputDevice& Log ) { #if TRACK_BULKDATA_USE // Arrays about to hold per object and per class size information. TArray<FObjectAndSize> PerObjectSizeArray; TArray<FObjectAndSize> PerClassSizeArray; // Iterate over all "live" bulk data and add size to arrays if it is loaded. for( TMap<FUntypedBulkData*,UObject*>::TIterator It(BulkDataToObjectMap); It; ++It ) { FUntypedBulkData* BulkData = It.Key(); UObject* Owner = It.Value(); // Only add bulk data that is consuming memory to array. if( BulkData->IsBulkDataLoaded() && BulkData->GetBulkDataSize() > 0 ) { // Per object stats. PerObjectSizeArray.Add( FObjectAndSize( Owner, BulkData->GetBulkDataSize() ) ); // Per class stats. bool bFoundExistingPerClassSize = false; // Iterate over array, trying to find existing entry. for( int32 PerClassIndex=0; PerClassIndex<PerClassSizeArray.Num(); PerClassIndex++ ) { FObjectAndSize& PerClassSize = PerClassSizeArray( PerClassIndex ); // Add to existing entry if found. if( PerClassSize.Object == Owner->GetClass() ) { PerClassSize.Size += BulkData->GetBulkDataSize(); bFoundExistingPerClassSize = true; break; } } // Add new entry if we didn't find an existing one. if( !bFoundExistingPerClassSize ) { PerClassSizeArray.Add( FObjectAndSize( Owner->GetClass(), BulkData->GetBulkDataSize() ) ); } } } /** Compare operator, sorting by size in descending order */ struct FCompareFObjectAndSize { FORCEINLINE bool operator()( const FObjectAndSize& A, const FObjectAndSize& B ) const { return B.Size < A.Size; } }; // Sort by size. PerObjectSizeArray.Sort( FCompareFObjectAndSize() ); PerClassSizeArray.Sort( FCompareFObjectAndSize() ); // Log information. UE_LOG(LogSerialization, Log, TEXT("")); UE_LOG(LogSerialization, Log, TEXT("Per class summary of bulk data use:")); for( int32 PerClassIndex=0; PerClassIndex<PerClassSizeArray.Num(); PerClassIndex++ ) { const FObjectAndSize& PerClassSize = PerClassSizeArray( PerClassIndex ); Log.Logf( TEXT(" %5d KByte of bulk data for Class %s"), PerClassSize.Size / 1024, *PerClassSize.Object->GetPathName() ); } UE_LOG(LogSerialization, Log, TEXT("")); UE_LOG(LogSerialization, Log, TEXT("Detailed per object stats of bulk data use:")); for( int32 PerObjectIndex=0; PerObjectIndex<PerObjectSizeArray.Num(); PerObjectIndex++ ) { const FObjectAndSize& PerObjectSize = PerObjectSizeArray( PerObjectIndex ); Log.Logf( TEXT(" %5d KByte of bulk data for %s"), PerObjectSize.Size / 1024, *PerObjectSize.Object->GetFullName() ); } UE_LOG(LogSerialization, Log, TEXT("")); #else UE_LOG(LogSerialization, Log, TEXT("Please recompiled with TRACK_BULKDATA_USE set to 1 in UnBulkData.cpp.")); #endif }