int32 USoundClassGraph::RecursivelyConstructChildNodes(USoundClassGraphNode* ParentNode, const TMap<USoundClass*, int32>& InChildCounts, bool bSelectNewNode/* = true*/) { const int32 HorizontalSpacing = 400; const int32 VerticalSpacing = 100; USoundClass* ParentClass = ParentNode->SoundClass; int32 TotalChildSizeY = InChildCounts.FindChecked(ParentClass) * VerticalSpacing; int32 NodeStartY = ParentNode->NodePosY - (TotalChildSizeY * 0.5f) + (VerticalSpacing * 0.5f); int32 NodePosX = ParentNode->NodePosX + HorizontalSpacing; for (int32 ChildIndex = 0; ChildIndex < ParentClass->ChildClasses.Num(); ChildIndex++) { if (ParentClass->ChildClasses[ChildIndex]) { const int32 ChildCount = InChildCounts.FindChecked(ParentClass->ChildClasses[ChildIndex]); int32 NodePosY = NodeStartY + (ChildCount * VerticalSpacing * 0.5f) - (VerticalSpacing * 0.5f); USoundClassGraphNode* ChildNode = CreateNode(ParentClass->ChildClasses[ChildIndex], NodePosX, NodePosY, bSelectNewNode); ParentNode->GetChildPin()->MakeLinkTo(ChildNode->GetParentPin()); RecursivelyConstructChildNodes(ChildNode, InChildCounts); NodeStartY += ChildCount * VerticalSpacing; } } return TotalChildSizeY; }
void FCameraLensSettingsCustomization::CustomizeChildren(TSharedRef<IPropertyHandle> StructPropertyHandle, class IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) { // Retrieve structure's child properties uint32 NumChildren; StructPropertyHandle->GetNumChildren(NumChildren); TMap<FName, TSharedPtr< IPropertyHandle > > PropertyHandles; for (uint32 ChildIndex = 0; ChildIndex < NumChildren; ++ChildIndex) { TSharedRef<IPropertyHandle> ChildHandle = StructPropertyHandle->GetChildHandle(ChildIndex).ToSharedRef(); const FName PropertyName = ChildHandle->GetProperty()->GetFName(); PropertyHandles.Add(PropertyName, ChildHandle); } // Retrieve special case properties MinFocalLengthHandle = PropertyHandles.FindChecked(GET_MEMBER_NAME_CHECKED(FCameraLensSettings, MinFocalLength)); MaxFocalLengthHandle = PropertyHandles.FindChecked(GET_MEMBER_NAME_CHECKED(FCameraLensSettings, MaxFocalLength)); MinFStopHandle = PropertyHandles.FindChecked(GET_MEMBER_NAME_CHECKED(FCameraLensSettings, MinFStop)); MaxFStopHandle = PropertyHandles.FindChecked(GET_MEMBER_NAME_CHECKED(FCameraLensSettings, MaxFStop)); MinFocusDistanceHandle = PropertyHandles.FindChecked(GET_MEMBER_NAME_CHECKED(FCameraLensSettings, MinimumFocusDistance)); for (auto Iter(PropertyHandles.CreateConstIterator()); Iter; ++Iter) { if (Iter.Value() == MinFocusDistanceHandle) { // skip showing these in the panel for now, as we don't really use them continue; } IDetailPropertyRow& SettingsRow = ChildBuilder.AddChildProperty(Iter.Value().ToSharedRef()); } }
void UFaceFXMatineeControl::GetTrackKeyForTime(float InTime, TArray<TPair<int32, const FFaceFXTrackKey*>>& OutResult, TArray<FFaceFXSkelMeshComponentId>* OutNoTracks) const { //build a list of all keys for all skelmesh component ids TMap<int32, TArray<const FFaceFXTrackKey*>> SkelMeshTracks; TMap<int32, FFaceFXSkelMeshComponentId> SkelMeshIds; for(const FFaceFXTrackKey& Key : Keys) { SkelMeshTracks.FindOrAdd(Key.SkelMeshComponentId.Index).Add(&Key); if(OutNoTracks && !SkelMeshIds.Contains(Key.SkelMeshComponentId.Index)) { SkelMeshIds.Add(Key.SkelMeshComponentId.Index, Key.SkelMeshComponentId); } } //then generate the pair results for each skelmesh component for(auto It = SkelMeshTracks.CreateConstIterator(); It; ++It) { const TArray<const FFaceFXTrackKey*>& SkelMeshKeys = It.Value(); const int32 IndexMax = SkelMeshKeys.Num()-1; int32 Index = INDEX_NONE; for(; Index < IndexMax && SkelMeshKeys[Index+1]->Time <= InTime; ++Index); if(Index != INDEX_NONE) { OutResult.Add(TPairInitializer<int32, const FFaceFXTrackKey*>(Index, SkelMeshKeys[Index])); } else if(OutNoTracks) { OutNoTracks->Add(SkelMeshIds.FindChecked(It.Key())); } } }
FGeometry SWidget::FindChildGeometry( const FGeometry& MyGeometry, TSharedRef<SWidget> WidgetToFind ) const { // We just need to find the one WidgetToFind among our descendants. TSet< TSharedRef<SWidget> > WidgetsToFind; { WidgetsToFind.Add( WidgetToFind ); } TMap<TSharedRef<SWidget>, FArrangedWidget> Result; FindChildGeometries( MyGeometry, WidgetsToFind, Result ); return Result.FindChecked( WidgetToFind ).Geometry; }
void FCameraFilmbackSettingsCustomization::CustomizeChildren(TSharedRef<IPropertyHandle> StructPropertyHandle, class IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) { // Retrieve structure's child properties uint32 NumChildren; StructPropertyHandle->GetNumChildren( NumChildren ); TMap<FName, TSharedPtr< IPropertyHandle > > PropertyHandles; for( uint32 ChildIndex = 0; ChildIndex < NumChildren; ++ChildIndex ) { TSharedRef<IPropertyHandle> ChildHandle = StructPropertyHandle->GetChildHandle( ChildIndex ).ToSharedRef(); const FName PropertyName = ChildHandle->GetProperty()->GetFName(); PropertyHandles.Add(PropertyName, ChildHandle); } // Retrieve special case properties SensorWidthHandle = PropertyHandles.FindChecked(GET_MEMBER_NAME_CHECKED(FCameraFilmbackSettings, SensorWidth)); SensorHeightHandle = PropertyHandles.FindChecked(GET_MEMBER_NAME_CHECKED(FCameraFilmbackSettings, SensorHeight)); for( auto Iter(PropertyHandles.CreateConstIterator()); Iter; ++Iter ) { IDetailPropertyRow& SettingsRow = ChildBuilder.AddChildProperty(Iter.Value().ToSharedRef()); } }
void FIndirectLightingCache::UpdateCachePrimitive( const TMap<FPrimitiveComponentId, FAttachmentGroupSceneInfo>& AttachmentGroups, FPrimitiveSceneInfo* PrimitiveSceneInfo, bool bAllowUnbuiltPreview, bool bOpaqueRelevance, TMap<FIntVector, FBlockUpdateInfo>& BlocksToUpdate, TArray<FIndirectLightingCacheAllocation*>& TransitionsOverTimeToUpdate) { FPrimitiveSceneProxy* PrimitiveSceneProxy = PrimitiveSceneInfo->Proxy; FIndirectLightingCacheAllocation** PrimitiveAllocationPtr = PrimitiveAllocations.Find(PrimitiveSceneInfo->PrimitiveComponentId); FIndirectLightingCacheAllocation* PrimitiveAllocation = PrimitiveAllocationPtr != NULL ? *PrimitiveAllocationPtr : NULL; const bool bIsMovable = PrimitiveSceneProxy->IsMovable(); if (PrimitiveSceneProxy->WillEverBeLit() && ((bAllowUnbuiltPreview && PrimitiveSceneProxy->HasStaticLighting() && PrimitiveAllocation && PrimitiveAllocation->bIsDirty) || (PrimitiveSceneProxy->IsMovable() && PrimitiveSceneProxy->GetIndirectLightingCacheQuality() != ILCQ_Off))) { const FIndirectLightingCacheAllocation* AttachmentParentAllocation = NULL; if (PrimitiveSceneInfo->LightingAttachmentRoot.IsValid()) { const FAttachmentGroupSceneInfo& AttachmentGroup = AttachmentGroups.FindChecked(PrimitiveSceneInfo->LightingAttachmentRoot); if (AttachmentGroup.ParentSceneInfo && AttachmentGroup.ParentSceneInfo->Proxy->LightAttachmentsAsGroup()) { AttachmentParentAllocation = FindPrimitiveAllocation(AttachmentGroup.ParentSceneInfo->PrimitiveComponentId); } } if (AttachmentParentAllocation) { // Reuse the attachment parent's lighting allocation if part of an attachment group PrimitiveSceneInfo->IndirectLightingCacheAllocation = AttachmentParentAllocation; // Don't know here if this parent ILC is or will be dirty or not. Always update. PrimitiveSceneInfo->MarkPrecomputedLightingBufferDirty(); } else { FIndirectLightingCacheAllocation* OriginalAllocation = PrimitiveAllocation; const bool bUnbuiltPreview = bAllowUnbuiltPreview && !bIsMovable; const bool bPointSample = PrimitiveSceneProxy->GetIndirectLightingCacheQuality() == ILCQ_Point || bUnbuiltPreview || !bOpaqueRelevance; const int32 BlockSize = bPointSample ? 1 : GLightingCacheMovableObjectAllocationSize; // Light with the cumulative bounds of the entire attachment group const bool bUpdated = UpdateCacheAllocation(PrimitiveSceneInfo->GetAttachmentGroupBounds(), BlockSize, bPointSample, bUnbuiltPreview, PrimitiveAllocation, BlocksToUpdate, TransitionsOverTimeToUpdate); // Cache the primitive allocation pointer on the FPrimitiveSceneInfo for base pass rendering PrimitiveSceneInfo->IndirectLightingCacheAllocation = PrimitiveAllocation; if (OriginalAllocation != PrimitiveAllocation) { if (OriginalAllocation) { PrimitiveAllocations.Remove(PrimitiveSceneInfo->PrimitiveComponentId); } // Allocate space in the atlas for this primitive and add it to a map, whose key is the component, so the allocation will persist through a re-register PrimitiveAllocations.Add(PrimitiveSceneInfo->PrimitiveComponentId, PrimitiveAllocation); } if (bUpdated) { PrimitiveSceneInfo->MarkPrecomputedLightingBufferDirty(); } } } }
void FStatsMemoryDumpCommand::ProcessMemoryOperations( const TMap<int64, FStatPacketArray>& CombinedHistory ) { // This is only example code, no fully implemented, may sometimes crash. // This code is not optimized. double PreviousSeconds = FPlatformTime::Seconds(); uint64 NumMemoryOperations = 0; // Generate frames TArray<int64> Frames; CombinedHistory.GenerateKeyArray( Frames ); Frames.Sort(); // Raw stats callstack for this stat packet array. TMap<FName, FStackState> StackStates; // All allocation ordered by the sequence tag. // There is an assumption that the sequence tag will not turn-around. //TMap<uint32, FAllocationInfo> SequenceAllocationMap; TArray<FAllocationInfo> SequenceAllocationArray; // Pass 1. // Read all stats messages, parse all memory operations and decode callstacks. const int64 FirstFrame = 0; PreviousSeconds -= NumSecondsBetweenLogs; for( int32 FrameIndex = 0; FrameIndex < Frames.Num(); ++FrameIndex ) { { const double CurrentSeconds = FPlatformTime::Seconds(); if( CurrentSeconds > PreviousSeconds + NumSecondsBetweenLogs ) { UE_LOG( LogStats, Warning, TEXT( "Processing frame %i/%i" ), FrameIndex+1, Frames.Num() ); PreviousSeconds = CurrentSeconds; } } const int64 TargetFrame = Frames[FrameIndex]; const int64 Diff = TargetFrame - FirstFrame; const FStatPacketArray& Frame = CombinedHistory.FindChecked( TargetFrame ); bool bAtLeastOnePacket = false; for( int32 PacketIndex = 0; PacketIndex < Frame.Packets.Num(); PacketIndex++ ) { { const double CurrentSeconds = FPlatformTime::Seconds(); if( CurrentSeconds > PreviousSeconds + NumSecondsBetweenLogs ) { UE_LOG( LogStats, Log, TEXT( "Processing packet %i/%i" ), PacketIndex, Frame.Packets.Num() ); PreviousSeconds = CurrentSeconds; bAtLeastOnePacket = true; } } const FStatPacket& StatPacket = *Frame.Packets[PacketIndex]; const FName& ThreadFName = StatsThreadStats.Threads.FindChecked( StatPacket.ThreadId ); const uint32 NewThreadID = ThreadIDtoStatID.FindChecked( StatPacket.ThreadId ); FStackState* StackState = StackStates.Find( ThreadFName ); if( !StackState ) { StackState = &StackStates.Add( ThreadFName ); StackState->Stack.Add( ThreadFName ); StackState->Current = ThreadFName; } const FStatMessagesArray& Data = StatPacket.StatMessages; int32 LastPct = 0; const int32 NumDataElements = Data.Num(); const int32 OnerPercent = FMath::Max( NumDataElements / 100, 1024 ); bool bAtLeastOneMessage = false; for( int32 Index = 0; Index < NumDataElements; Index++ ) { if( Index % OnerPercent ) { const double CurrentSeconds = FPlatformTime::Seconds(); if( CurrentSeconds > PreviousSeconds + NumSecondsBetweenLogs ) { const int32 CurrentPct = int32( 100.0*(Index + 1) / NumDataElements ); UE_LOG( LogStats, Log, TEXT( "Processing %3i%% (%i/%i) stat messages" ), CurrentPct, Index, NumDataElements ); PreviousSeconds = CurrentSeconds; bAtLeastOneMessage = true; } } const FStatMessage& Item = Data[Index]; const EStatOperation::Type Op = Item.NameAndInfo.GetField<EStatOperation>(); const FName RawName = Item.NameAndInfo.GetRawName(); if( Op == EStatOperation::CycleScopeStart || Op == EStatOperation::CycleScopeEnd || Op == EStatOperation::Memory ) { if( Op == EStatOperation::CycleScopeStart ) { StackState->Stack.Add( RawName ); StackState->Current = RawName; } else if( Op == EStatOperation::Memory ) { // Experimental code used only to test the implementation. // First memory operation is Alloc or Free const uint64 EncodedPtr = Item.GetValue_Ptr(); const bool bIsAlloc = (EncodedPtr & (uint64)EMemoryOperation::Alloc) != 0; const bool bIsFree = (EncodedPtr & (uint64)EMemoryOperation::Free) != 0; const uint64 Ptr = EncodedPtr & ~(uint64)EMemoryOperation::Mask; if( bIsAlloc ) { NumMemoryOperations++; // @see FStatsMallocProfilerProxy::TrackAlloc // After alloc ptr message there is always alloc size message and the sequence tag. Index++; const FStatMessage& AllocSizeMessage = Data[Index]; const int64 AllocSize = AllocSizeMessage.GetValue_int64(); // Read operation sequence tag. Index++; const FStatMessage& SequenceTagMessage = Data[Index]; const uint32 SequenceTag = SequenceTagMessage.GetValue_int64(); // Create a callstack. TArray<FName> StatsBasedCallstack; for( const auto& StackName : StackState->Stack ) { StatsBasedCallstack.Add( StackName ); } // Add a new allocation. SequenceAllocationArray.Add( FAllocationInfo( Ptr, AllocSize, StatsBasedCallstack, SequenceTag, EMemoryOperation::Alloc, StackState->bIsBrokenCallstack ) ); } else if( bIsFree ) { NumMemoryOperations++; // Read operation sequence tag. Index++; const FStatMessage& SequenceTagMessage = Data[Index]; const uint32 SequenceTag = SequenceTagMessage.GetValue_int64(); // Create a callstack. /* TArray<FName> StatsBasedCallstack; for( const auto& RawName : StackState->Stack ) { StatsBasedCallstack.Add( RawName ); } */ // Add a new free. SequenceAllocationArray.Add( FAllocationInfo( Ptr, 0, TArray<FName>()/*StatsBasedCallstack*/, SequenceTag, EMemoryOperation::Free, StackState->bIsBrokenCallstack ) ); } else { UE_LOG( LogStats, Warning, TEXT( "Pointer from a memory operation is invalid" ) ); } } else if( Op == EStatOperation::CycleScopeEnd ) { if( StackState->Stack.Num() > 1 ) { const FName ScopeStart = StackState->Stack.Pop(); const FName ScopeEnd = Item.NameAndInfo.GetRawName(); check( ScopeStart == ScopeEnd ); StackState->Current = StackState->Stack.Last(); // The stack should be ok, but it may be partially broken. // This will happen if memory profiling starts in the middle of executing a background thread. StackState->bIsBrokenCallstack = false; } else { const FName ShortName = Item.NameAndInfo.GetShortName(); UE_LOG( LogStats, Warning, TEXT( "Broken cycle scope end %s/%s, current %s" ), *ThreadFName.ToString(), *ShortName.ToString(), *StackState->Current.ToString() ); // The stack is completely broken, only has the thread name and the last cycle scope. // Rollback to the thread node. StackState->bIsBrokenCallstack = true; StackState->Stack.Empty(); StackState->Stack.Add( ThreadFName ); StackState->Current = ThreadFName; } } } } if( bAtLeastOneMessage ) { PreviousSeconds -= NumSecondsBetweenLogs; } } if( bAtLeastOnePacket ) { PreviousSeconds -= NumSecondsBetweenLogs; } } UE_LOG( LogStats, Warning, TEXT( "NumMemoryOperations: %llu" ), NumMemoryOperations ); UE_LOG( LogStats, Warning, TEXT( "SequenceAllocationNum: %i" ), SequenceAllocationArray.Num() ); // Pass 2. /* TMap<uint32,FAllocationInfo> UniqueSeq; TMultiMap<uint32,FAllocationInfo> OriginalAllocs; TMultiMap<uint32,FAllocationInfo> BrokenAllocs; for( const FAllocationInfo& Alloc : SequenceAllocationArray ) { const FAllocationInfo* Found = UniqueSeq.Find(Alloc.SequenceTag); if( !Found ) { UniqueSeq.Add(Alloc.SequenceTag,Alloc); } else { OriginalAllocs.Add(Alloc.SequenceTag, *Found); BrokenAllocs.Add(Alloc.SequenceTag, Alloc); } } */ // Sort all memory operation by the sequence tag, iterate through all operation and generate memory usage. SequenceAllocationArray.Sort( TLess<FAllocationInfo>() ); // Alive allocations. TMap<uint64, FAllocationInfo> AllocationMap; TMultiMap<uint64, FAllocationInfo> FreeWithoutAllocMap; TMultiMap<uint64, FAllocationInfo> DuplicatedAllocMap; int32 NumDuplicatedMemoryOperations = 0; int32 NumFWAMemoryOperations = 0; // FreeWithoutAlloc UE_LOG( LogStats, Warning, TEXT( "Generating memory operations map" ) ); const int32 NumSequenceAllocations = SequenceAllocationArray.Num(); const int32 OnePercent = FMath::Max( NumSequenceAllocations / 100, 1024 ); for( int32 Index = 0; Index < NumSequenceAllocations; Index++ ) { if( Index % OnePercent ) { const double CurrentSeconds = FPlatformTime::Seconds(); if( CurrentSeconds > PreviousSeconds + NumSecondsBetweenLogs ) { const int32 CurrentPct = int32( 100.0*(Index + 1) / NumSequenceAllocations ); UE_LOG( LogStats, Log, TEXT( "Processing allocations %3i%% (%10i/%10i)" ), CurrentPct, Index + 1, NumSequenceAllocations ); PreviousSeconds = CurrentSeconds; } } const FAllocationInfo& Alloc = SequenceAllocationArray[Index]; const EMemoryOperation MemOp = Alloc.Op; const uint64 Ptr = Alloc.Ptr; const int64 Size = Alloc.Size; const uint32 SequenceTag = Alloc.SequenceTag; if( MemOp == EMemoryOperation::Alloc ) { const FAllocationInfo* Found = AllocationMap.Find( Ptr ); if( !Found ) { AllocationMap.Add( Ptr, Alloc ); } else { const FAllocationInfo* FoundAndFreed = FreeWithoutAllocMap.Find( Found->Ptr ); const FAllocationInfo* FoundAndAllocated = FreeWithoutAllocMap.Find( Alloc.Ptr ); #if _DEBUG if( FoundAndFreed ) { const FString FoundAndFreedCallstack = GetCallstack( FoundAndFreed->EncodedCallstack ); } if( FoundAndAllocated ) { const FString FoundAndAllocatedCallstack = GetCallstack( FoundAndAllocated->EncodedCallstack ); } NumDuplicatedMemoryOperations++; const FString FoundCallstack = GetCallstack( Found->EncodedCallstack ); const FString AllocCallstack = GetCallstack( Alloc.EncodedCallstack ); #endif // _DEBUG // Replace pointer. AllocationMap.Add( Ptr, Alloc ); // Store the old pointer. DuplicatedAllocMap.Add( Ptr, *Found ); } } else if( MemOp == EMemoryOperation::Free ) { const FAllocationInfo* Found = AllocationMap.Find( Ptr ); if( Found ) { const bool bIsValid = Alloc.SequenceTag > Found->SequenceTag; if( !bIsValid ) { UE_LOG( LogStats, Warning, TEXT( "InvalidFree Ptr: %llu, Seq: %i/%i" ), Ptr, SequenceTag, Found->SequenceTag ); } AllocationMap.Remove( Ptr ); } else { FreeWithoutAllocMap.Add( Ptr, Alloc ); NumFWAMemoryOperations++; } } } UE_LOG( LogStats, Warning, TEXT( "NumDuplicatedMemoryOperations: %i" ), NumDuplicatedMemoryOperations ); UE_LOG( LogStats, Warning, TEXT( "NumFWAMemoryOperations: %i" ), NumFWAMemoryOperations ); // Dump problematic allocations DuplicatedAllocMap.ValueSort( FAllocationInfoGreater() ); //FreeWithoutAllocMap uint64 TotalDuplicatedMemory = 0; for( const auto& It : DuplicatedAllocMap ) { const FAllocationInfo& Alloc = It.Value; TotalDuplicatedMemory += Alloc.Size; } UE_LOG( LogStats, Warning, TEXT( "Dumping duplicated alloc map" ) ); const float MaxPctDisplayed = 0.80f; uint64 DisplayedSoFar = 0; for( const auto& It : DuplicatedAllocMap ) { const FAllocationInfo& Alloc = It.Value; const FString AllocCallstack = GetCallstack( Alloc.EncodedCallstack ); UE_LOG( LogStats, Log, TEXT( "%lli (%.2f MB) %s" ), Alloc.Size, Alloc.Size / 1024.0f / 1024.0f, *AllocCallstack ); DisplayedSoFar += Alloc.Size; const float CurrentPct = (float)DisplayedSoFar / (float)TotalDuplicatedMemory; if( CurrentPct > MaxPctDisplayed ) { break; } } GenerateMemoryUsageReport( AllocationMap ); }
void FAttenuationSettingsCustomization::CustomizeChildren( TSharedRef<IPropertyHandle> StructPropertyHandle, class IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils ) { uint32 NumChildren; StructPropertyHandle->GetNumChildren( NumChildren ); TMap<FName, TSharedPtr< IPropertyHandle > > PropertyHandles; for( uint32 ChildIndex = 0; ChildIndex < NumChildren; ++ChildIndex ) { TSharedRef<IPropertyHandle> ChildHandle = StructPropertyHandle->GetChildHandle( ChildIndex ).ToSharedRef(); const FName PropertyName = ChildHandle->GetProperty()->GetFName(); PropertyHandles.Add(PropertyName, ChildHandle); } // We'll set up reset to default ourselves const bool bDisplayResetToDefault = false; const FString DisplayNameOverride = TEXT(""); AttenuationShapeHandle = PropertyHandles.FindChecked(GET_MEMBER_NAME_CHECKED(FAttenuationSettings, AttenuationShape)); DistanceAlgorithmHandle = PropertyHandles.FindChecked(GET_MEMBER_NAME_CHECKED(FAttenuationSettings, DistanceAlgorithm)); SpatializationAlgorithmHandle = PropertyHandles.FindChecked(GET_MEMBER_NAME_CHECKED(FAttenuationSettings, SpatializationAlgorithm)); TSharedRef<IPropertyHandle> AttenuationExtentsHandle = PropertyHandles.FindChecked(GET_MEMBER_NAME_CHECKED(FAttenuationSettings, AttenuationShapeExtents)).ToSharedRef(); uint32 NumExtentChildren; AttenuationExtentsHandle->GetNumChildren( NumExtentChildren ); TSharedPtr< IPropertyHandle > ExtentXHandle; TSharedPtr< IPropertyHandle > ExtentYHandle; TSharedPtr< IPropertyHandle > ExtentZHandle; for( uint32 ExtentChildIndex = 0; ExtentChildIndex < NumExtentChildren; ++ExtentChildIndex ) { TSharedRef<IPropertyHandle> ChildHandle = AttenuationExtentsHandle->GetChildHandle( ExtentChildIndex ).ToSharedRef(); const FName PropertyName = ChildHandle->GetProperty()->GetFName(); if (PropertyName == GET_MEMBER_NAME_CHECKED(FVector, X)) { ExtentXHandle = ChildHandle; } else if (PropertyName == GET_MEMBER_NAME_CHECKED(FVector, Y)) { ExtentYHandle = ChildHandle; } else { check(PropertyName == GET_MEMBER_NAME_CHECKED(FVector, Z)); ExtentZHandle = ChildHandle; } } ChildBuilder.AddChildProperty(PropertyHandles.FindChecked(GET_MEMBER_NAME_CHECKED(FAttenuationSettings, bAttenuate)).ToSharedRef()); ChildBuilder.AddChildProperty(PropertyHandles.FindChecked(GET_MEMBER_NAME_CHECKED(FAttenuationSettings, bSpatialize)).ToSharedRef()); ChildBuilder.AddChildProperty(DistanceAlgorithmHandle.ToSharedRef() ); // Check to see if a spatialization plugin is enabled if (IsAudioPluginEnabled(EAudioPlugin::SPATIALIZATION)) { ChildBuilder.AddChildProperty(SpatializationAlgorithmHandle.ToSharedRef()); } IDetailPropertyRow& CustomCurveRow = ChildBuilder.AddChildProperty(PropertyHandles.FindChecked(GET_MEMBER_NAME_CHECKED(FAttenuationSettings, CustomAttenuationCurve)).ToSharedRef()); CustomCurveRow.Visibility(TAttribute<EVisibility>(this, &FAttenuationSettingsCustomization::IsCustomCurveSelected)); IDetailPropertyRow& dbAttenuationRow = ChildBuilder.AddChildProperty(PropertyHandles.FindChecked(GET_MEMBER_NAME_CHECKED(FAttenuationSettings, dBAttenuationAtMax)).ToSharedRef()); dbAttenuationRow.Visibility(TAttribute<EVisibility>(this, &FAttenuationSettingsCustomization::IsNaturalSoundSelected)); IDetailPropertyRow& AttenuationShapeRow = ChildBuilder.AddChildProperty( AttenuationShapeHandle.ToSharedRef() ); ChildBuilder.AddChildProperty(AttenuationExtentsHandle) .Visibility(TAttribute<EVisibility>(this, &FAttenuationSettingsCustomization::IsBoxSelected)) .DisplayName(NSLOCTEXT("AttenuationSettings", "BoxExtentsLabel", "Extents")) .ToolTip(NSLOCTEXT("AttenuationSettings", "BoxExtents", "The dimensions of the of the box.")); ChildBuilder.AddChildContent(NSLOCTEXT("AttenuationSettings", "RadiusLabel", "Radius")) .NameContent() [ SNew(STextBlock) .Text(NSLOCTEXT("AttenuationSettings", "RadiusLabel", "Radius")) .ToolTipText(NSLOCTEXT("AttenuationSettings", "RadiusToolTip", "The distance from the location of the sound at which falloff begins.")) .Font(StructCustomizationUtils.GetRegularFont()) ] .ValueContent() [ ExtentXHandle->CreatePropertyValueWidget() ] .Visibility(TAttribute<EVisibility>(this, &FAttenuationSettingsCustomization::IsSphereSelected)); ChildBuilder.AddChildContent(NSLOCTEXT("AttenuationSettings", "CapsuleHalfHeightLabel", "Capsule Half Height")) .NameContent() [ SNew(STextBlock) .Text(NSLOCTEXT("AttenuationSettings", "CapsuleHalfHeightLabel", "Capsule Half Height")) .ToolTipText(NSLOCTEXT("AttenuationSettings", "CapsuleHalfHeightToolTip", "The attenuation capsule's half height.")) .Font(StructCustomizationUtils.GetRegularFont()) ] .ValueContent() [ ExtentXHandle->CreatePropertyValueWidget() ] .Visibility(TAttribute<EVisibility>(this, &FAttenuationSettingsCustomization::IsCapsuleSelected)); ChildBuilder.AddChildContent(NSLOCTEXT("AttenuationSettings", "CapsuleRadiusLabel", "Capsule Radius")) .NameContent() [ SNew(STextBlock) .Text(NSLOCTEXT("AttenuationSettings", "CapsuleRadiusLabel", "Capsule Radius")) .ToolTipText(NSLOCTEXT("AttenuationSettings", "CapsuleRadiusToolTip", "The attenuation capsule's radius.")) .Font(StructCustomizationUtils.GetRegularFont()) ] .ValueContent() [ ExtentYHandle->CreatePropertyValueWidget() ] .Visibility(TAttribute<EVisibility>(this, &FAttenuationSettingsCustomization::IsCapsuleSelected)); ChildBuilder.AddChildContent(NSLOCTEXT("AttenuationSettings", "ConeRadiusLabel", "Cone Radius")) .NameContent() [ SNew(STextBlock) .Text(NSLOCTEXT("AttenuationSettings", "ConeRadiusLabel", "Cone Radius")) .ToolTipText(NSLOCTEXT("AttenuationSettings", "ConeRadiusToolTip", "The attenuation cone's radius.")) .Font(StructCustomizationUtils.GetRegularFont()) ] .ValueContent() [ ExtentXHandle->CreatePropertyValueWidget() ] .Visibility(TAttribute<EVisibility>(this, &FAttenuationSettingsCustomization::IsConeSelected)); ChildBuilder.AddChildContent(NSLOCTEXT("AttenuationSettings", "ConeAngleLabel", "Cone Angle")) .NameContent() [ SNew(STextBlock) .Text(NSLOCTEXT("AttenuationSettings", "ConeAngleLabel", "Cone Angle")) .ToolTipText(NSLOCTEXT("AttenuationSettings", "ConeAngleToolTip", "The angle of the inner edge of the attenuation cone's falloff. Inside this angle sounds will be at full volume.")) .Font(StructCustomizationUtils.GetRegularFont()) ] .ValueContent() [ ExtentYHandle->CreatePropertyValueWidget() ] .Visibility(TAttribute<EVisibility>(this, &FAttenuationSettingsCustomization::IsConeSelected)); ChildBuilder.AddChildContent(NSLOCTEXT("AttenuationSettings", "ConeFalloffAngleLabel", "Cone Falloff Angle")) .NameContent() [ SNew(STextBlock) .Text(NSLOCTEXT("AttenuationSettings", "ConeFalloffAngleLabel", "Cone Falloff Angle")) .ToolTipText(NSLOCTEXT("AttenuationSettings", "ConeFalloffAngleToolTip", "The angle of the outer edge of the attenuation cone's falloff. Outside this angle sounds will be inaudible.")) .Font(StructCustomizationUtils.GetRegularFont()) ] .ValueContent() [ ExtentZHandle->CreatePropertyValueWidget() ] .Visibility(TAttribute<EVisibility>(this, &FAttenuationSettingsCustomization::IsConeSelected)); IDetailPropertyRow& ConeOffsetRow = ChildBuilder.AddChildProperty(PropertyHandles.FindChecked(GET_MEMBER_NAME_CHECKED(FAttenuationSettings, ConeOffset)).ToSharedRef()); ConeOffsetRow.Visibility(TAttribute<EVisibility>(this, &FAttenuationSettingsCustomization::IsConeSelected)); ChildBuilder.AddChildProperty(PropertyHandles.FindChecked(GET_MEMBER_NAME_CHECKED(FAttenuationSettings, FalloffDistance)).ToSharedRef()); ChildBuilder.AddChildProperty(PropertyHandles.FindChecked(GET_MEMBER_NAME_CHECKED(FAttenuationSettings, OmniRadius)).ToSharedRef()); ChildBuilder.AddChildProperty(PropertyHandles.FindChecked(GET_MEMBER_NAME_CHECKED(FAttenuationSettings, bAttenuateWithLPF)).ToSharedRef()); ChildBuilder.AddChildProperty(PropertyHandles.FindChecked(GET_MEMBER_NAME_CHECKED(FAttenuationSettings, LPFRadiusMin)).ToSharedRef()); ChildBuilder.AddChildProperty(PropertyHandles.FindChecked(GET_MEMBER_NAME_CHECKED(FAttenuationSettings, LPFRadiusMax)).ToSharedRef()); if (PropertyHandles.Num() != 14) { FString PropertyList; for (auto It(PropertyHandles.CreateConstIterator()); It; ++It) { PropertyList += It.Key().ToString() + TEXT(", "); } ensureMsgf(false, TEXT("Unexpected property handle(s) customizing FAttenuationSettings: %s"), *PropertyList); } }
void BuildResourceTableMapping( const TMap<FString,FResourceTableEntry>& ResourceTableMap, const TMap<FString,uint32>& ResourceTableLayoutHashes, TBitArray<>& UsedUniformBufferSlots, FShaderParameterMap& ParameterMap, FShaderResourceTable& OutSRT) { check(OutSRT.ResourceTableBits == 0); check(OutSRT.ResourceTableLayoutHashes.Num() == 0); // Build resource table mapping int32 MaxBoundResourceTable = -1; TArray<uint32> ResourceTableSRVs; TArray<uint32> ResourceTableSamplerStates; TArray<uint32> ResourceTableUAVs; for( auto MapIt = ResourceTableMap.CreateConstIterator(); MapIt; ++MapIt ) { const FString& Name = MapIt->Key; const FResourceTableEntry& Entry = MapIt->Value; uint16 BufferIndex, BaseIndex, Size; if (ParameterMap.FindParameterAllocation( *Name, BufferIndex, BaseIndex, Size ) ) { ParameterMap.RemoveParameterAllocation(*Name); uint16 UniformBufferIndex = INDEX_NONE, UBBaseIndex, UBSize; if (ParameterMap.FindParameterAllocation(*Entry.UniformBufferName, UniformBufferIndex, UBBaseIndex, UBSize) == false) { UniformBufferIndex = UsedUniformBufferSlots.FindAndSetFirstZeroBit(); ParameterMap.AddParameterAllocation(*Entry.UniformBufferName,UniformBufferIndex,0,0); } OutSRT.ResourceTableBits |= (1 << UniformBufferIndex); MaxBoundResourceTable = FMath::Max<int32>(MaxBoundResourceTable, (int32)UniformBufferIndex); while (OutSRT.ResourceTableLayoutHashes.Num() <= MaxBoundResourceTable) { OutSRT.ResourceTableLayoutHashes.Add(0); } OutSRT.ResourceTableLayoutHashes[UniformBufferIndex] = ResourceTableLayoutHashes.FindChecked(Entry.UniformBufferName); auto ResourceMap = FRHIResourceTableEntry::Create(UniformBufferIndex, Entry.ResourceIndex, BaseIndex); switch( Entry.Type ) { case UBMT_TEXTURE: OutSRT.TextureMap.Add(ResourceMap); break; case UBMT_SAMPLER: OutSRT.SamplerMap.Add(ResourceMap); break; case UBMT_SRV: OutSRT.ShaderResourceViewMap.Add(ResourceMap); break; case UBMT_UAV: OutSRT.UnorderedAccessViewMap.Add(ResourceMap); break; default: check(0); } } } OutSRT.MaxBoundResourceTable = MaxBoundResourceTable; }
bool FVisualStudioSourceCodeAccessor::AddSourceFiles(const TArray<FString>& AbsoluteSourcePaths, const TArray<FString>& AvailableModules) { // This requires DTE - there is no fallback for this operation when DTE is not available #if VSACCESSOR_HAS_DTE bool bSuccess = true; struct FModuleNameAndPath { FString ModuleBuildFilePath; FString ModulePath; FName ModuleName; }; TArray<FModuleNameAndPath> ModuleNamesAndPaths; ModuleNamesAndPaths.Reserve(AvailableModules.Num()); for (const FString& AvailableModule : AvailableModules) { static const int32 BuildFileExtensionLen = FString(TEXT(".Build.cs")).Len(); // AvailableModule is the relative path to the .Build.cs file FModuleNameAndPath ModuleNameAndPath; ModuleNameAndPath.ModuleBuildFilePath = FPaths::ConvertRelativePathToFull(AvailableModule); ModuleNameAndPath.ModulePath = FPaths::GetPath(ModuleNameAndPath.ModuleBuildFilePath); ModuleNameAndPath.ModuleName = *FPaths::GetCleanFilename(ModuleNameAndPath.ModuleBuildFilePath).LeftChop(BuildFileExtensionLen); ModuleNamesAndPaths.Add(ModuleNameAndPath); } struct FModuleNewSourceFiles { FModuleNameAndPath ModuleNameAndPath; TArray<FString> NewSourceFiles; }; // Work out which module each source file will be in TMap<FName, FModuleNewSourceFiles> ModuleToNewSourceFiles; { const FModuleNameAndPath* LastSourceFilesModule = nullptr; for (const FString& SourceFile : AbsoluteSourcePaths) { // First check to see if this source file is in the same module as the last source file - this is usually the case, and saves us a lot of string compares if (LastSourceFilesModule && SourceFile.StartsWith(LastSourceFilesModule->ModulePath)) { FModuleNewSourceFiles& ModuleNewSourceFiles = ModuleToNewSourceFiles.FindChecked(LastSourceFilesModule->ModuleName); ModuleNewSourceFiles.NewSourceFiles.Add(SourceFile); continue; } // Look for the module which will contain this file LastSourceFilesModule = nullptr; for (const FModuleNameAndPath& ModuleNameAndPath : ModuleNamesAndPaths) { if (SourceFile.StartsWith(ModuleNameAndPath.ModulePath)) { LastSourceFilesModule = &ModuleNameAndPath; FModuleNewSourceFiles& ModuleNewSourceFiles = ModuleToNewSourceFiles.FindOrAdd(ModuleNameAndPath.ModuleName); ModuleNewSourceFiles.ModuleNameAndPath = ModuleNameAndPath; ModuleNewSourceFiles.NewSourceFiles.Add(SourceFile); break; } } // Failed to find the module for this source file? if (!LastSourceFilesModule) { UE_LOG(LogVSAccessor, Warning, TEXT("Cannot add source file '%s' as it doesn't belong to a known module"), *SourceFile); bSuccess = false; } } } TComPtr<EnvDTE::_DTE> DTE; const FString SolutionPath = GetSolutionPath(); if (AccessVisualStudioViaDTE(DTE, SolutionPath, GetPrioritizedVisualStudioVersions(SolutionPath)) == EAccessVisualStudioResult::VSInstanceIsOpen) { TComPtr<EnvDTE::_Solution> Solution; if (SUCCEEDED(DTE->get_Solution(&Solution)) && Solution) { // Process each module for (const auto& ModuleNewSourceFilesKeyValue : ModuleToNewSourceFiles) { const FModuleNewSourceFiles& ModuleNewSourceFiles = ModuleNewSourceFilesKeyValue.Value; const FString& ModuleBuildFilePath = ModuleNewSourceFiles.ModuleNameAndPath.ModuleBuildFilePath; auto ANSIModuleBuildFilePath = StringCast<ANSICHAR>(*ModuleBuildFilePath); FComBSTR COMStrModuleBuildFilePath(ANSIModuleBuildFilePath.Get()); TComPtr<EnvDTE::ProjectItem> BuildFileProjectItem; if (SUCCEEDED(Solution->FindProjectItem(COMStrModuleBuildFilePath, &BuildFileProjectItem)) && BuildFileProjectItem) { // We found the .Build.cs file in the existing solution - now we need its parent ProjectItems as that's what we'll be adding new content to TComPtr<EnvDTE::ProjectItems> ModuleProjectFolder; if (SUCCEEDED(BuildFileProjectItem->get_Collection(&ModuleProjectFolder)) && ModuleProjectFolder) { for (const FString& SourceFile : AbsoluteSourcePaths) { const FString ProjectRelativeSourceFilePath = SourceFile.Mid(ModuleNewSourceFiles.ModuleNameAndPath.ModulePath.Len()); TArray<FString> SourceFileParts; ProjectRelativeSourceFilePath.ParseIntoArray(SourceFileParts, TEXT("/"), true); if (SourceFileParts.Num() == 0) { // This should never happen as it means we somehow have no filename within the project directory bSuccess = false; continue; } TComPtr<EnvDTE::ProjectItems> CurProjectItems = ModuleProjectFolder; // Firstly we need to make sure that all the folders we need exist - this also walks us down to the correct place to add the file for (int32 FilePartIndex = 0; FilePartIndex < SourceFileParts.Num() - 1 && CurProjectItems; ++FilePartIndex) { const FString& SourceFilePart = SourceFileParts[FilePartIndex]; auto ANSIPart = StringCast<ANSICHAR>(*SourceFilePart); FComBSTR COMStrFilePart(ANSIPart.Get()); ::VARIANT vProjectItemName; vProjectItemName.vt = VT_BSTR; vProjectItemName.bstrVal = COMStrFilePart; TComPtr<EnvDTE::ProjectItem> ProjectItem; if (SUCCEEDED(CurProjectItems->Item(vProjectItemName, &ProjectItem)) && !ProjectItem) { // Add this part CurProjectItems->AddFolder(COMStrFilePart, nullptr, &ProjectItem); } if (ProjectItem) { ProjectItem->get_ProjectItems(&CurProjectItems); } else { CurProjectItems = nullptr; } } if (!CurProjectItems) { // Failed to find or add all the path parts bSuccess = false; continue; } // Now we add the file to the project under the last folder we found along its path auto ANSIPath = StringCast<ANSICHAR>(*SourceFile); FComBSTR COMStrFileName(ANSIPath.Get()); TComPtr<EnvDTE::ProjectItem> FileProjectItem; if (SUCCEEDED(CurProjectItems->AddFromFile(COMStrFileName, &FileProjectItem))) { bSuccess &= true; } } // Save the updated project to avoid a message when closing VS TComPtr<EnvDTE::Project> Project; if (SUCCEEDED(ModuleProjectFolder->get_ContainingProject(&Project)) && Project) { Project->Save(nullptr); } } else { UE_LOG(LogVSAccessor, Warning, TEXT("Cannot add source files as we failed to get the parent items container for the '%s' item"), *ModuleBuildFilePath); bSuccess = false; } } else { UE_LOG(LogVSAccessor, Warning, TEXT("Cannot add source files as we failed to find '%s' in the solution"), *ModuleBuildFilePath); bSuccess = false; } } } else { UE_LOG(LogVSAccessor, Warning, TEXT("Cannot add source files as Visual Studio failed to return a solution when queried")); bSuccess = false; } } else { UE_LOG(LogVSAccessor, Verbose, TEXT("Cannot add source files as Visual Studio is either not open or not responding")); bSuccess = false; } return bSuccess; #endif return false; }
void FProfilerStatMetaData::UpdateFromStatsState( const FStatsThreadState& StatsThreadStats ) { TMap<FName, int32> GroupFNameIDs; for( auto It = StatsThreadStats.Threads.CreateConstIterator(); It; ++It ) { ThreadDescriptions.Add( It.Key(), It.Value().ToString() ); } const uint32 NoGroupID = 0; const uint32 ThreadGroupID = 1; // Special groups. InitializeGroup( NoGroupID, "NoGroup" ); // Self must be 0. InitializeStat( 0, NoGroupID, TEXT( "Self" ), STATTYPE_CycleCounter ); // ThreadRoot must be 1. InitializeStat( 1, NoGroupID, FStatConstants::NAME_ThreadRoot.GetPlainNameString(), STATTYPE_CycleCounter, FStatConstants::NAME_ThreadRoot ); int32 UniqueID = 15; TArray<FName> GroupFNames; StatsThreadStats.Groups.MultiFind( NAME_Groups, GroupFNames ); for( const auto& GroupFName : GroupFNames ) { UniqueID++; InitializeGroup( UniqueID, GroupFName.ToString() ); GroupFNameIDs.Add( GroupFName, UniqueID ); } for( auto It = StatsThreadStats.ShortNameToLongName.CreateConstIterator(); It; ++It ) { const FStatMessage& LongName = It.Value(); const FName GroupName = LongName.NameAndInfo.GetGroupName(); if( GroupName == NAME_Groups ) { continue; } const int32 GroupID = GroupFNameIDs.FindChecked( GroupName ); const FName StatName = It.Key(); UniqueID++; EStatType StatType = STATTYPE_Error; if( LongName.NameAndInfo.GetField<EStatDataType>() == EStatDataType::ST_int64 ) { if( LongName.NameAndInfo.GetFlag( EStatMetaFlags::IsCycle ) ) { StatType = STATTYPE_CycleCounter; } else if( LongName.NameAndInfo.GetFlag( EStatMetaFlags::IsMemory ) ) { StatType = STATTYPE_MemoryCounter; } else { StatType = STATTYPE_AccumulatorDWORD; } } else if( LongName.NameAndInfo.GetField<EStatDataType>() == EStatDataType::ST_double ) { StatType = STATTYPE_AccumulatorFLOAT; } else if( LongName.NameAndInfo.GetField<EStatDataType>() == EStatDataType::ST_Ptr ) { // Not supported at this moment. continue; } check( StatType != STATTYPE_Error ); int32 StatID = UniqueID; // Some hackery. if( StatName == TEXT( "STAT_FrameTime" ) ) { StatID = 2; } const FString Description = LongName.NameAndInfo.GetDescription(); const FString StatDesc = !Description.IsEmpty() ? Description : StatName.ToString(); InitializeStat( StatID, GroupID, StatDesc, StatType, StatName ); // Setup thread id to stat id. if( GroupName == FStatConstants::NAME_ThreadGroup ) { uint32 ThreadID = 0; for( auto ThreadsIt = StatsThreadStats.Threads.CreateConstIterator(); ThreadsIt; ++ThreadsIt ) { if (ThreadsIt.Value() == StatName) { ThreadID = ThreadsIt.Key(); break; } } ThreadIDtoStatID.Add( ThreadID, StatID ); // Game thread is always NAME_GameThread if( StatName == NAME_GameThread ) { GameThreadID = ThreadID; } // Rendering thread may be "Rendering thread" or NAME_RenderThread with an index else if( StatName.GetPlainNameString().Contains( FName(NAME_RenderThread).GetPlainNameString() ) ) { RenderThreadIDs.AddUnique( ThreadID ); } else if( StatName.GetPlainNameString().Contains( TEXT( "RenderingThread" ) ) ) { RenderThreadIDs.AddUnique( ThreadID ); } } } }
void FRawProfilerSession::PrepareLoading() { SCOPE_LOG_TIME_FUNC(); const FString Filepath = DataFilepath + FStatConstants::StatsFileRawExtension; const int64 Size = IFileManager::Get().FileSize( *Filepath ); if( Size < 4 ) { UE_LOG( LogStats, Error, TEXT( "Could not open: %s" ), *Filepath ); return; } TAutoPtr<FArchive> FileReader( IFileManager::Get().CreateFileReader( *Filepath ) ); if( !FileReader ) { UE_LOG( LogStats, Error, TEXT( "Could not open: %s" ), *Filepath ); return; } if( !Stream.ReadHeader( *FileReader ) ) { UE_LOG( LogStats, Error, TEXT( "Could not open, bad magic: %s" ), *Filepath ); return; } const bool bIsFinalized = Stream.Header.IsFinalized(); check( bIsFinalized ); check( Stream.Header.Version == EStatMagicWithHeader::VERSION_5 ); StatsThreadStats.MarkAsLoaded(); TArray<FStatMessage> Messages; if( Stream.Header.bRawStatsFile ) { // Read metadata. TArray<FStatMessage> MetadataMessages; Stream.ReadFNamesAndMetadataMessages( *FileReader, MetadataMessages ); StatsThreadStats.ProcessMetaDataOnly( MetadataMessages ); const FName F00245 = FName(245, 245, 0); const FName F11602 = FName(11602, 11602, 0); const FName F06394 = FName(6394, 6394, 0); const int64 CurrentFilePos = FileReader->Tell(); // Update profiler's metadata. StatMetaData->UpdateFromStatsState( StatsThreadStats ); const uint32 GameThreadID = GetMetaData()->GetGameThreadID(); // Read frames offsets. Stream.ReadFramesOffsets( *FileReader ); // Buffer used to store the compressed and decompressed data. TArray<uint8> SrcArray; TArray<uint8> DestArray; const bool bHasCompressedData = Stream.Header.HasCompressedData(); check(bHasCompressedData); TMap<int64, FStatPacketArray> CombinedHistory; int64 TotalPacketSize = 0; int64 MaximumPacketSize = 0; // Read all packets sequentially, force by the memory profiler which is now a part of the raw stats. // !!CAUTION!! Frame number in the raw stats is pointless, because it is time based, not frame based. // Background threads usually execute time consuming operations, so the frame number won't be valid. // Needs to be combined by the thread and the time, not by the frame number. { int64 FrameOffset0 = Stream.FramesInfo[0].FrameFileOffset; FileReader->Seek( FrameOffset0 ); const int64 FileSize = FileReader->TotalSize(); while( FileReader->Tell() < FileSize ) { // Read the compressed data. FCompressedStatsData UncompressedData( SrcArray, DestArray ); *FileReader << UncompressedData; if( UncompressedData.HasReachedEndOfCompressedData() ) { break; } FMemoryReader MemoryReader( DestArray, true ); FStatPacket* StatPacket = new FStatPacket(); Stream.ReadStatPacket( MemoryReader, *StatPacket ); const int64 FrameNum = StatPacket->Frame; FStatPacketArray& Frame = CombinedHistory.FindOrAdd(FrameNum); // Check if we need to combine packets from the same thread. FStatPacket** CombinedPacket = Frame.Packets.FindByPredicate([&](FStatPacket* Item) -> bool { return Item->ThreadId == StatPacket->ThreadId; }); if( CombinedPacket ) { (*CombinedPacket)->StatMessages += StatPacket->StatMessages; } else { Frame.Packets.Add(StatPacket); } const int64 CurrentPos = FileReader->Tell(); const int32 PctPos = int32(100.0f*CurrentPos/FileSize); UE_LOG( LogStats, Log, TEXT( "%3i Processing FStatPacket: Frame %5i for thread %5i with %6i messages (%.1f MB)" ), PctPos, StatPacket->Frame, StatPacket->ThreadId, StatPacket->StatMessages.Num(), StatPacket->StatMessages.GetAllocatedSize()/1024.0f/1024.0f ); const int64 PacketSize = StatPacket->StatMessages.GetAllocatedSize(); TotalPacketSize += PacketSize; MaximumPacketSize = FMath::Max( MaximumPacketSize, PacketSize ); } } UE_LOG( LogStats, Log, TEXT( "TotalPacketSize: %.1f MB, Max: %1f MB" ), TotalPacketSize/1024.0f/1024.0f, MaximumPacketSize/1024.0f/1024.0f ); TArray<int64> Frames; CombinedHistory.GenerateKeyArray(Frames); Frames.Sort(); const int64 MiddleFrame = Frames[Frames.Num()/2]; // Remove all frames without the game thread messages. for (int32 FrameIndex = 0; FrameIndex < Frames.Num(); ++FrameIndex) { const int64 TargetFrame = Frames[FrameIndex]; const FStatPacketArray& Frame = CombinedHistory.FindChecked( TargetFrame ); const double GameThreadTimeMS = GetMetaData()->ConvertCyclesToMS( GetFastThreadFrameTimeInternal( Frame, EThreadType::Game ) ); if (GameThreadTimeMS == 0.0f) { CombinedHistory.Remove( TargetFrame ); Frames.RemoveAt( FrameIndex ); FrameIndex--; } } StatMetaData->SecondsPerCycle = GetSecondsPerCycle( CombinedHistory.FindChecked(MiddleFrame) ); check( StatMetaData->GetSecondsPerCycle() > 0.0 ); //const int32 FirstGameThreadFrame = FindFirstFrameWithGameThread( CombinedHistory, Frames ); // Prepare profiler frame. { SCOPE_LOG_TIME( TEXT( "Preparing profiler frames" ), nullptr ); // Prepare profiler frames. double ElapsedTimeMS = 0; for( int32 FrameIndex = 0; FrameIndex < Frames.Num(); ++FrameIndex ) { const int64 TargetFrame = Frames[FrameIndex]; const FStatPacketArray& Frame = CombinedHistory.FindChecked(TargetFrame); const double GameThreadTimeMS = GetMetaData()->ConvertCyclesToMS( GetFastThreadFrameTimeInternal(Frame,EThreadType::Game) ); if( GameThreadTimeMS == 0.0f ) { continue; } const double RenderThreadTimeMS = GetMetaData()->ConvertCyclesToMS( GetFastThreadFrameTimeInternal(Frame,EThreadType::Renderer) ); // Update mini-view, convert from cycles to ms. TMap<uint32, float> ThreadTimesMS; ThreadTimesMS.Add( GameThreadID, GameThreadTimeMS ); ThreadTimesMS.Add( GetMetaData()->GetRenderThreadID()[0], RenderThreadTimeMS ); // Pass the reference to the stats' metadata. OnAddThreadTime.ExecuteIfBound( FrameIndex, ThreadTimesMS, StatMetaData ); // Create a new profiler frame and add it to the stream. ElapsedTimeMS += GameThreadTimeMS; FProfilerFrame* ProfilerFrame = new FProfilerFrame( TargetFrame, GameThreadTimeMS, ElapsedTimeMS ); ProfilerFrame->ThreadTimesMS = ThreadTimesMS; ProfilerStream.AddProfilerFrame( TargetFrame, ProfilerFrame ); } } // Process the raw stats data. { SCOPE_LOG_TIME( TEXT( "Processing the raw stats" ), nullptr ); double CycleCounterAdjustmentMS = 0.0f; // Read the raw stats messages. for( int32 FrameIndex = 0; FrameIndex < Frames.Num()-1; ++FrameIndex ) { const int64 TargetFrame = Frames[FrameIndex]; const FStatPacketArray& Frame = CombinedHistory.FindChecked(TargetFrame); FProfilerFrame* ProfilerFrame = ProfilerStream.GetProfilerFrame( FrameIndex ); UE_CLOG( FrameIndex % 8 == 0, LogStats, Log, TEXT( "Processing raw stats frame: %4i/%4i" ), FrameIndex, Frames.Num() ); ProcessStatPacketArray( Frame, *ProfilerFrame, FrameIndex ); // or ProfilerFrame->TargetFrame // Find the first cycle counter for the game thread. if( CycleCounterAdjustmentMS == 0.0f ) { CycleCounterAdjustmentMS = ProfilerFrame->Root->CycleCounterStartTimeMS; } // Update thread time and mark profiler frame as valid and ready for use. ProfilerFrame->MarkAsValid(); } // Adjust all profiler frames. ProfilerStream.AdjustCycleCounters( CycleCounterAdjustmentMS ); } } const int64 AllocatedSize = ProfilerStream.GetAllocatedSize(); // We have the whole metadata and basic information about the raw stats file, start ticking the profiler session. //OnTickHandle = FTicker::GetCoreTicker().AddTicker( OnTick, 0.25f ); #if 0 if( SessionType == EProfilerSessionTypes::OfflineRaw ) { // Broadcast that a capture file has been fully processed. OnCaptureFileProcessed.ExecuteIfBound( GetInstanceID() ); } #endif // 0 }
void FRawProfilerSession::ProcessStatPacketArray( const FStatPacketArray& StatPacketArray, FProfilerFrame& out_ProfilerFrame, int32 FrameIndex ) { // @TODO yrx 2014-03-24 Standardize thread names and id // @TODO yrx 2014-04-22 Remove all references to the data provider, event graph etc once data graph can visualize. // Raw stats callstack for this stat packet array. TMap<FName,FProfilerStackNode*> ThreadNodes; const FProfilerStatMetaDataRef MetaData = GetMetaData(); FProfilerSampleArray& MutableCollection = const_cast<FProfilerSampleArray&>(DataProvider->GetCollection()); // Add a root sample for this frame. const uint32 FrameRootSampleIndex = DataProvider->AddHierarchicalSample( 0, MetaData->GetStatByID( 1 ).OwningGroup().ID(), 1, 0.0f, 0.0f, 1 ); // Iterate through all stats packets and raw stats messages. FName GameThreadFName = NAME_None; for( int32 PacketIndex = 0; PacketIndex < StatPacketArray.Packets.Num(); PacketIndex++ ) { const FStatPacket& StatPacket = *StatPacketArray.Packets[PacketIndex]; FName ThreadFName = StatsThreadStats.Threads.FindChecked( StatPacket.ThreadId ); const uint32 NewThreadID = MetaData->ThreadIDtoStatID.FindChecked( StatPacket.ThreadId ); // @TODO yrx 2014-04-29 Only game or render thread is supported at this moment. if( StatPacket.ThreadType != EThreadType::Game && StatPacket.ThreadType != EThreadType::Renderer ) { continue; } // Workaround for issue with rendering thread names. if( StatPacket.ThreadType == EThreadType::Renderer ) { ThreadFName = NAME_RenderThread; } else if( StatPacket.ThreadType == EThreadType::Game ) { GameThreadFName = ThreadFName; } FProfilerStackNode* ThreadNode = ThreadNodes.FindRef( ThreadFName ); if( !ThreadNode ) { FString ThreadIdName = FStatsUtils::BuildUniqueThreadName( StatPacket.ThreadId ); FStatMessage ThreadMessage( ThreadFName, EStatDataType::ST_int64, STAT_GROUP_TO_FStatGroup( STATGROUP_Threads )::GetGroupName(), STAT_GROUP_TO_FStatGroup( STATGROUP_Threads )::GetGroupCategory(), *ThreadIdName, true, true ); //FStatMessage ThreadMessage( ThreadFName, EStatDataType::ST_int64, nullptr, nullptr, TEXT( "" ), true, true ); ThreadMessage.NameAndInfo.SetFlag( EStatMetaFlags::IsPackedCCAndDuration, true ); ThreadMessage.Clear(); // Add a thread sample. const uint32 ThreadRootSampleIndex = DataProvider->AddHierarchicalSample ( NewThreadID, MetaData->GetStatByID( NewThreadID ).OwningGroup().ID(), NewThreadID, -1.0f, -1.0f, 1, FrameRootSampleIndex ); ThreadNode = ThreadNodes.Add( ThreadFName, new FProfilerStackNode( nullptr, ThreadMessage, ThreadRootSampleIndex, FrameIndex ) ); } TArray<const FStatMessage*> StartStack; TArray<FProfilerStackNode*> Stack; Stack.Add( ThreadNode ); FProfilerStackNode* Current = Stack.Last(); const FStatMessagesArray& Data = StatPacket.StatMessages; for( int32 Index = 0; Index < Data.Num(); Index++ ) { const FStatMessage& Item = Data[Index]; const EStatOperation::Type Op = Item.NameAndInfo.GetField<EStatOperation>(); const FName LongName = Item.NameAndInfo.GetRawName(); const FName ShortName = Item.NameAndInfo.GetShortName(); const FName RenderingThreadTickCommandName = TEXT("RenderingThreadTickCommand"); // Workaround for render thread hierarchy. EStatOperation::AdvanceFrameEventRenderThread is called within the scope. if( ShortName == RenderingThreadTickCommandName ) { continue; } if( Op == EStatOperation::CycleScopeStart || Op == EStatOperation::CycleScopeEnd || Op == EStatOperation::AdvanceFrameEventRenderThread ) { //check( Item.NameAndInfo.GetFlag( EStatMetaFlags::IsCycle ) ); if( Op == EStatOperation::CycleScopeStart ) { FProfilerStackNode* ChildNode = new FProfilerStackNode( Current, Item, -1, FrameIndex ); Current->Children.Add( ChildNode ); // Add a child sample. const uint32 SampleIndex = DataProvider->AddHierarchicalSample ( NewThreadID, MetaData->GetStatByFName( ShortName ).OwningGroup().ID(), // GroupID MetaData->GetStatByFName( ShortName ).ID(), // StatID MetaData->ConvertCyclesToMS( ChildNode->CyclesStart ), // StartMS MetaData->ConvertCyclesToMS( 0 ), // DurationMS 1, Current->SampleIndex ); ChildNode->SampleIndex = SampleIndex; Stack.Add( ChildNode ); StartStack.Add( &Item ); Current = ChildNode; } // Workaround for render thread hierarchy. EStatOperation::AdvanceFrameEventRenderThread is called within the scope. if( Op == EStatOperation::AdvanceFrameEventRenderThread ) { int k=0;k++; } if( Op == EStatOperation::CycleScopeEnd ) { const FStatMessage ScopeStart = *StartStack.Pop(); const FStatMessage ScopeEnd = Item; const int64 Delta = int32( uint32( ScopeEnd.GetValue_int64() ) - uint32( ScopeStart.GetValue_int64() ) ); Current->CyclesEnd = Current->CyclesStart + Delta; Current->CycleCounterStartTimeMS = MetaData->ConvertCyclesToMS( Current->CyclesStart ); Current->CycleCounterEndTimeMS = MetaData->ConvertCyclesToMS( Current->CyclesEnd ); if( Current->CycleCounterStartTimeMS > Current->CycleCounterEndTimeMS ) { int k=0;k++; } check( Current->CycleCounterEndTimeMS >= Current->CycleCounterStartTimeMS ); FProfilerStackNode* ChildNode = Current; // Update the child sample's DurationMS. MutableCollection[ChildNode->SampleIndex].SetDurationMS( MetaData->ConvertCyclesToMS( Delta ) ); verify( Current == Stack.Pop() ); Current = Stack.Last(); } } } } // Calculate thread times. for( auto It = ThreadNodes.CreateIterator(); It; ++It ) { FProfilerStackNode& ThreadNode = *It.Value(); const int32 ChildrenNum = ThreadNode.Children.Num(); if( ChildrenNum > 0 ) { const int32 LastChildIndex = ThreadNode.Children.Num() - 1; ThreadNode.CyclesStart = ThreadNode.Children[0]->CyclesStart; ThreadNode.CyclesEnd = ThreadNode.Children[LastChildIndex]->CyclesEnd; ThreadNode.CycleCounterStartTimeMS = MetaData->ConvertCyclesToMS( ThreadNode.CyclesStart ); ThreadNode.CycleCounterEndTimeMS = MetaData->ConvertCyclesToMS( ThreadNode.CyclesEnd ); FProfilerSample& ProfilerSample = MutableCollection[ThreadNode.SampleIndex]; ProfilerSample.SetStartAndEndMS( MetaData->ConvertCyclesToMS( ThreadNode.CyclesStart ), MetaData->ConvertCyclesToMS( ThreadNode.CyclesEnd ) ); } } // Get the game thread time. check( GameThreadFName != NAME_None ); const FProfilerStackNode& GameThreadNode = *ThreadNodes.FindChecked( GameThreadFName ); const double GameThreadStartMS = MetaData->ConvertCyclesToMS( GameThreadNode.CyclesStart ); const double GameThreadEndMS = MetaData->ConvertCyclesToMS( GameThreadNode.CyclesEnd ); MutableCollection[FrameRootSampleIndex].SetStartAndEndMS( GameThreadStartMS, GameThreadEndMS ); // Advance frame const uint32 LastFrameIndex = DataProvider->GetNumFrames(); DataProvider->AdvanceFrame( GameThreadEndMS - GameThreadStartMS ); // Update aggregated stats UpdateAggregatedStats( LastFrameIndex ); // Update aggregated events. UpdateAggregatedEventGraphData( LastFrameIndex ); // RootNode is the same as the game thread node. out_ProfilerFrame.Root->CycleCounterStartTimeMS = GameThreadStartMS; out_ProfilerFrame.Root->CycleCounterEndTimeMS = GameThreadEndMS; for( auto It = ThreadNodes.CreateIterator(); It; ++It ) { out_ProfilerFrame.AddChild( It.Value() ); } out_ProfilerFrame.SortChildren(); }
virtual void Compile(FKismetFunctionContext& Context, UEdGraphNode* Node) { ///////////////////////////////////////////////////////////////////////////////////// // Get the node, retrieve the helper functions, and create a local "Index" variable ///////////////////////////////////////////////////////////////////////////////////// // Get the multi gate node and the helper functions UK2Node_MultiGate* GateNode = Cast<UK2Node_MultiGate>(Node); // Get function names and class pointers to helper nodes FName MarkBitFunctionName = ""; UClass* MarkBitFunctionClass = NULL; GateNode->GetMarkBitFunction(MarkBitFunctionName, &MarkBitFunctionClass); UFunction* MarkBitFunction = FindField<UFunction>(MarkBitFunctionClass, MarkBitFunctionName); FName HasUnmarkedBitFunctionName = ""; UClass* HasUnmarkedBitFunctionClass = NULL; GateNode->GetHasUnmarkedBitFunction(HasUnmarkedBitFunctionName, &HasUnmarkedBitFunctionClass); UFunction* HasUnmarkedBitFunction = FindField<UFunction>(HasUnmarkedBitFunctionClass, HasUnmarkedBitFunctionName); FName GetUnmarkedBitFunctionName = ""; UClass* GetUnmarkedBitFunctionClass = NULL; GateNode->GetUnmarkedBitFunction(GetUnmarkedBitFunctionName, &GetUnmarkedBitFunctionClass); UFunction* GetUnmarkedBitFunction = FindField<UFunction>(GetUnmarkedBitFunctionClass, GetUnmarkedBitFunctionName); FName ConditionalFunctionName = ""; UClass* ConditionalFunctionClass = NULL; GateNode->GetConditionalFunction(ConditionalFunctionName, &ConditionalFunctionClass); UFunction* ConditionFunction = FindField<UFunction>(ConditionalFunctionClass, ConditionalFunctionName); FName EqualityFunctionName = ""; UClass* EqualityFunctionClass = NULL; GateNode->GetEqualityFunction(EqualityFunctionName, &EqualityFunctionClass); UFunction* EqualityFunction = FindField<UFunction>(EqualityFunctionClass, EqualityFunctionName); FName BoolNotEqualFunctionName = ""; UClass* BoolNotEqualFunctionClass = NULL; GateNode->GetBoolNotEqualFunction(BoolNotEqualFunctionName, &BoolNotEqualFunctionClass); UFunction* BoolNotEqualFunction = FindField<UFunction>(BoolNotEqualFunctionClass, BoolNotEqualFunctionName); FName PrintStringFunctionName = ""; UClass* PrintStringFunctionClass = NULL; GateNode->GetPrintStringFunction(PrintStringFunctionName, &PrintStringFunctionClass); UFunction* PrintFunction = FindField<UFunction>(PrintStringFunctionClass, PrintStringFunctionName); FName ClearBitsFunctionName = ""; UClass* ClearBitsFunctionClass = NULL; GateNode->GetClearAllBitsFunction(ClearBitsFunctionName, &ClearBitsFunctionClass); UFunction* ClearBitsFunction = FindField<UFunction>(ClearBitsFunctionClass, ClearBitsFunctionName); // Find the data terms if there is already a data node from expansion phase FBPTerminal* DataTerm = NULL; if (GateNode->DataNode) { UEdGraphPin* PinToTry = FEdGraphUtilities::GetNetFromPin(GateNode->DataNode->GetVariablePin()); FBPTerminal** DataTermPtr = Context.NetMap.Find(PinToTry); DataTerm = (DataTermPtr != NULL) ? *DataTermPtr : NULL; } // Else we built it in the net registration, so find it else { DataTerm = DataTermMap.FindRef(GateNode); } check(DataTerm); // Used for getting all the nets from pins UEdGraphPin* PinToTry = NULL; // The StartIndex passed into the multi gate node PinToTry = FEdGraphUtilities::GetNetFromPin(GateNode->GetStartIndexPin()); FBPTerminal** StartIndexPinTerm = Context.NetMap.Find(PinToTry); // Get the bRandom pin as a kismet term from the multi gate node PinToTry = FEdGraphUtilities::GetNetFromPin(GateNode->GetIsRandomPin()); FBPTerminal** RandomTerm = Context.NetMap.Find(PinToTry); // Get the Loop pin as a kismet term from the multi gate node PinToTry = FEdGraphUtilities::GetNetFromPin(GateNode->GetLoopPin()); FBPTerminal** LoopTerm = Context.NetMap.Find(PinToTry); // Find the local boolean for use in determining if this is the first run of the node or not FBPTerminal* FirstRunBoolTerm = FirstRunTermMap.FindRef(GateNode); // Create a literal pin that represents a -1 value FBPTerminal* InvalidIndexTerm = Context.CreateLocalTerminal(ETerminalSpecification::TS_Literal); InvalidIndexTerm->bIsLiteral = true; InvalidIndexTerm->Type.PinCategory = CompilerContext.GetSchema()->PC_Int; InvalidIndexTerm->Name = TEXT("-1"); // Create a literal pin that represents a true value FBPTerminal* TrueBoolTerm = Context.CreateLocalTerminal(ETerminalSpecification::TS_Literal); TrueBoolTerm->Type.PinCategory = CompilerContext.GetSchema()->PC_Boolean; TrueBoolTerm->bIsLiteral = true; TrueBoolTerm->Name = TEXT("true"); // Get the out pins and create a literal describing how many logical outs there are TArray<UEdGraphPin*> OutPins; GateNode->GetOutPins(OutPins); FBPTerminal* NumOutsTerm = Context.CreateLocalTerminal(ETerminalSpecification::TS_Literal); NumOutsTerm->Type.PinCategory = CompilerContext.GetSchema()->PC_Int; NumOutsTerm->bIsLiteral = true; NumOutsTerm->Name = FString::Printf(TEXT("%d"), OutPins.Num()); /////////////////////////////////////////////////// // See if this is the first time in /////////////////////////////////////////////////// FFunctionScopedTerms& FuncLocals = FunctionTermMap.FindChecked(Context.Function); check(FuncLocals.GenericBoolTerm != nullptr); // (bIsNotFirstTime != true) FBlueprintCompiledStatement& BoolNotEqualStatement = Context.AppendStatementForNode(Node); BoolNotEqualStatement.Type = KCST_CallFunction; BoolNotEqualStatement.FunctionToCall = BoolNotEqualFunction; BoolNotEqualStatement.FunctionContext = NULL; BoolNotEqualStatement.bIsParentContext = false; // Set the params BoolNotEqualStatement.LHS = FuncLocals.GenericBoolTerm; BoolNotEqualStatement.RHS.Add(FirstRunBoolTerm); BoolNotEqualStatement.RHS.Add(TrueBoolTerm); // if (bIsNotFirstTime == false) // { FBlueprintCompiledStatement& IfFirstTimeStatement = Context.AppendStatementForNode(Node); IfFirstTimeStatement.Type = KCST_GotoIfNot; IfFirstTimeStatement.LHS = FuncLocals.GenericBoolTerm; /////////////////////////////////////////////////////////////////// // This is the first time in... set the bool and the start index /////////////////////////////////////////////////////////////////// // bIsNotFirstTime = true; FBlueprintCompiledStatement& AssignBoolStatement = Context.AppendStatementForNode(Node); AssignBoolStatement.Type = KCST_Assignment; AssignBoolStatement.LHS = FirstRunBoolTerm; AssignBoolStatement.RHS.Add(TrueBoolTerm); ////////////////////////////////////////////////////////////////////// // See if the StartIndex is greater than -1 (they supplied an index) ////////////////////////////////////////////////////////////////////// // (StartIndex > -1) FBlueprintCompiledStatement& Statement = Context.AppendStatementForNode(Node); Statement.Type = KCST_CallFunction; Statement.FunctionToCall = ConditionFunction; Statement.FunctionContext = NULL; Statement.bIsParentContext = false; Statement.LHS = FuncLocals.GenericBoolTerm; Statement.RHS.Add(*StartIndexPinTerm); Statement.RHS.Add(InvalidIndexTerm); // if (StartIndex > -1) // { FBlueprintCompiledStatement& IfHasIndexStatement = Context.AppendStatementForNode(Node); IfHasIndexStatement.Type = KCST_GotoIfNot; IfHasIndexStatement.LHS = FuncLocals.GenericBoolTerm; /////////////////////////////////////////////////////////////////// // They supplied a start index so set the index to it /////////////////////////////////////////////////////////////////// // Index = StartIndex; // (StartIndex is from multi gate pin for it) FBlueprintCompiledStatement& AssignSuppliedIndexStatement = Context.AppendStatementForNode(Node); AssignSuppliedIndexStatement.Type = KCST_Assignment; AssignSuppliedIndexStatement.LHS = FuncLocals.IndexTerm; AssignSuppliedIndexStatement.RHS.Add(*StartIndexPinTerm); // Jump to index usage FBlueprintCompiledStatement& ElseGotoIndexUsageStatement = Context.AppendStatementForNode(Node); ElseGotoIndexUsageStatement.Type = KCST_UnconditionalGoto; // } // else // { /////////////////////////////////////////////////////////////////// // They did NOT supply a start index so figure one out /////////////////////////////////////////////////////////////////// check(FuncLocals.IndexTerm != nullptr); // Index = GetUnmarkedBit(Data, -1, bRandom); FBlueprintCompiledStatement& GetStartIndexStatement = Context.AppendStatementForNode(Node); GetStartIndexStatement.Type = KCST_CallFunction; GetStartIndexStatement.FunctionToCall = GetUnmarkedBitFunction; GetStartIndexStatement.bIsParentContext = false; GetStartIndexStatement.LHS = FuncLocals.IndexTerm; GetStartIndexStatement.RHS.Add(DataTerm); GetStartIndexStatement.RHS.Add(*StartIndexPinTerm); GetStartIndexStatement.RHS.Add(NumOutsTerm); GetStartIndexStatement.RHS.Add(*RandomTerm); // Hook the IfHasIndexStatement jump to this node GetStartIndexStatement.bIsJumpTarget = true; IfHasIndexStatement.TargetLabel = &GetStartIndexStatement; // Jump to index usage FBlueprintCompiledStatement& StartIndexGotoIndexUsageStatement = Context.AppendStatementForNode(Node); StartIndexGotoIndexUsageStatement.Type = KCST_UnconditionalGoto; // } // } // else // { //////////////////////////////////////////////////////////////////////////// // Else this is NOT the first time in, see if there is an available index //////////////////////////////////////////////////////////////////////////// // (HasUnmarkedBit()) FBlueprintCompiledStatement& IsAvailableStatement = Context.AppendStatementForNode(Node); IsAvailableStatement.Type = KCST_CallFunction; IsAvailableStatement.FunctionToCall = HasUnmarkedBitFunction; IsAvailableStatement.FunctionContext = NULL; IsAvailableStatement.bIsParentContext = false; IsAvailableStatement.LHS = FuncLocals.GenericBoolTerm; IsAvailableStatement.RHS.Add(DataTerm); IsAvailableStatement.RHS.Add(NumOutsTerm); // Hook the IfFirstTimeStatement jump to this node IsAvailableStatement.bIsJumpTarget = true; IfFirstTimeStatement.TargetLabel = &IsAvailableStatement; // if (HasUnmarkedBit()) // { FBlueprintCompiledStatement& IfIsAvailableStatement = Context.AppendStatementForNode(Node); IfIsAvailableStatement.Type = KCST_GotoIfNot; IfIsAvailableStatement.LHS = FuncLocals.GenericBoolTerm; //////////////////////////////////////////////////////////////////////////// // Has available index so figure it out and jump to its' usage //////////////////////////////////////////////////////////////////////////// // Index = GetUnmarkedBit(Data, -1, bRandom) FBlueprintCompiledStatement& GetNextIndexStatement = Context.AppendStatementForNode(Node); GetNextIndexStatement.Type = KCST_CallFunction; GetNextIndexStatement.FunctionToCall = GetUnmarkedBitFunction; GetNextIndexStatement.bIsParentContext = false; GetNextIndexStatement.LHS = FuncLocals.IndexTerm; GetNextIndexStatement.RHS.Add(DataTerm); GetNextIndexStatement.RHS.Add(*StartIndexPinTerm); GetNextIndexStatement.RHS.Add(NumOutsTerm); GetNextIndexStatement.RHS.Add(*RandomTerm); // Goto Index usage FBlueprintCompiledStatement& GotoIndexUsageStatement = Context.AppendStatementForNode(Node); GotoIndexUsageStatement.Type = KCST_UnconditionalGoto; // } // else // { //////////////////////////////////////////////////////////////////////////// // No available index, see if we can loop //////////////////////////////////////////////////////////////////////////// // if (bLoop) FBlueprintCompiledStatement& IfLoopingStatement = Context.AppendStatementForNode(Node); IfLoopingStatement.Type = KCST_GotoIfNot; IfLoopingStatement.LHS = *LoopTerm; IfLoopingStatement.bIsJumpTarget = true; IfIsAvailableStatement.TargetLabel = &IfLoopingStatement; // { //////////////////////////////////////////////////////////////////////////// // Reset the data and jump back up to "if (HasUnmarkedBit())" //////////////////////////////////////////////////////////////////////////// // Clear the data // Data = 0; FBlueprintCompiledStatement& ClearDataStatement = Context.AppendStatementForNode(Node); ClearDataStatement.Type = KCST_CallFunction; ClearDataStatement.FunctionToCall = ClearBitsFunction; ClearDataStatement.bIsParentContext = false; ClearDataStatement.RHS.Add(DataTerm); // Goto back up to attempt an index again FBlueprintCompiledStatement& RetryStatement = Context.AppendStatementForNode(Node); RetryStatement.Type = KCST_UnconditionalGoto; IsAvailableStatement.bIsJumpTarget = true; RetryStatement.TargetLabel = &IsAvailableStatement; // } // else // { //////////////////////////////////////////////////////////////////////////// // Dead... Jump to end of thread //////////////////////////////////////////////////////////////////////////// FBlueprintCompiledStatement& NoLoopStatement = Context.AppendStatementForNode(Node); NoLoopStatement.Type = KCST_EndOfThread; NoLoopStatement.bIsJumpTarget = true; IfLoopingStatement.TargetLabel = &NoLoopStatement; // } // } // } ////////////////////////////////////// // We have a valid index so mark it ////////////////////////////////////// // MarkBit(Data, Index); FBlueprintCompiledStatement& MarkIndexStatement = Context.AppendStatementForNode(Node); MarkIndexStatement.Type = KCST_CallFunction; MarkIndexStatement.FunctionToCall = MarkBitFunction; MarkIndexStatement.bIsParentContext = false; MarkIndexStatement.LHS = FuncLocals.IndexTerm; MarkIndexStatement.RHS.Add(DataTerm); MarkIndexStatement.RHS.Add(FuncLocals.IndexTerm); // Setup jump label MarkIndexStatement.bIsJumpTarget = true; GotoIndexUsageStatement.TargetLabel = &MarkIndexStatement; ElseGotoIndexUsageStatement.TargetLabel = &MarkIndexStatement; StartIndexGotoIndexUsageStatement.TargetLabel = &MarkIndexStatement; ///////////////////////////////////////////////////////////////////////// // We have a valid index so mark it, then find the correct exec out pin ///////////////////////////////////////////////////////////////////////// // Call the correct exec pin out of the multi gate node FBlueprintCompiledStatement* PrevIndexEqualityStatement = NULL; FBlueprintCompiledStatement* PrevIfIndexMatchesStatement = NULL; for (int32 OutIdx = 0; OutIdx < OutPins.Num(); OutIdx++) { // (Index == OutIdx) FBlueprintCompiledStatement& IndexEqualityStatement = Context.AppendStatementForNode(Node); IndexEqualityStatement.Type = KCST_CallFunction; IndexEqualityStatement.FunctionToCall = EqualityFunction; IndexEqualityStatement.FunctionContext = NULL; IndexEqualityStatement.bIsParentContext = false; // LiteralIndexTerm will be the right side of the == statemnt FBPTerminal* LiteralIndexTerm = Context.CreateLocalTerminal(ETerminalSpecification::TS_Literal); LiteralIndexTerm->bIsLiteral = true; LiteralIndexTerm->Type.PinCategory = CompilerContext.GetSchema()->PC_Int; LiteralIndexTerm->Name = FString::Printf(TEXT("%d"), OutIdx); // Set the params IndexEqualityStatement.LHS = FuncLocals.GenericBoolTerm; IndexEqualityStatement.RHS.Add(FuncLocals.IndexTerm); IndexEqualityStatement.RHS.Add(LiteralIndexTerm); // if (Index == OutIdx) FBlueprintCompiledStatement& IfIndexMatchesStatement = Context.AppendStatementForNode(Node); IfIndexMatchesStatement.Type = KCST_GotoIfNot; IfIndexMatchesStatement.LHS = FuncLocals.GenericBoolTerm; // { ////////////////////////////////////// // Found a match - Jump there ////////////////////////////////////// GenerateSimpleThenGoto(Context, *GateNode, OutPins[OutIdx]); // } // else // { //////////////////////////////////////////////////// // Not a match so loop will attempt the next index //////////////////////////////////////////////////// if (PrevIndexEqualityStatement && PrevIfIndexMatchesStatement) { // Attempt next index IndexEqualityStatement.bIsJumpTarget = true; PrevIfIndexMatchesStatement->TargetLabel = &IndexEqualityStatement; } // } PrevIndexEqualityStatement = &IndexEqualityStatement; PrevIfIndexMatchesStatement = &IfIndexMatchesStatement; } check(PrevIfIndexMatchesStatement); // Should have jumped to proper index, print error (should never happen) // Create a CallFunction statement for doing a print string of our error message FBlueprintCompiledStatement& PrintStatement = Context.AppendStatementForNode(Node); PrintStatement.Type = KCST_CallFunction; PrintStatement.bIsJumpTarget = true; PrintStatement.FunctionToCall = PrintFunction; PrintStatement.FunctionContext = NULL; PrintStatement.bIsParentContext = false; // Create a local int for use in the equality call function below (LiteralTerm = the right hand side of the EqualEqual_IntInt or NotEqual_BoolBool statement) FBPTerminal* LiteralStringTerm = Context.CreateLocalTerminal(ETerminalSpecification::TS_Literal); LiteralStringTerm->bIsLiteral = true; LiteralStringTerm->Type.PinCategory = CompilerContext.GetSchema()->PC_String; LiteralStringTerm->Name = FString::Printf(*LOCTEXT("MultiGateNode IndexWarning", "MultiGate Node failed! Out of bounds indexing of the out pins. There are only %d outs available.").ToString(), OutPins.Num()); PrintStatement.RHS.Add(LiteralStringTerm); // Hook the IfNot statement's jump target to this statement PrevIfIndexMatchesStatement->TargetLabel = &PrintStatement; }
UObject* USspjFactory::FactoryCreateBinary(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, const TCHAR* Type, const uint8*& Buffer, const uint8* InBufferEnd, FFeedbackContext* Warn) { bool bReimport = this->IsA(UReimportSspjFactory::StaticClass()); TMap<FString, UTexture*>* ExistImages = NULL; if(bReimport) { ExistImages = &(Cast<UReimportSspjFactory>(this)->ExistImages); } FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools"); FString ProjectNameStr = InName.ToString(); FName ProjectName = InName; UPackage* InParentPackage = Cast<UPackage>(InParent); if(InParentPackage && !bReimport) { FString ProjectPackageName; FString BasePackageName = FPackageName::GetLongPackagePath(InParent->GetOutermost()->GetName()) / ProjectNameStr; AssetToolsModule.Get().CreateUniqueAssetName(BasePackageName, TEXT(""), ProjectPackageName, ProjectNameStr); InParentPackage->Rename(*ProjectPackageName); } // インポート設定の取得 const USsImportSettings* ImportSettings = GetDefault<USsImportSettings>(); // インポート開始 FEditorDelegates::OnAssetPreImport.Broadcast(this, InClass, InParent, ProjectName, Type); // sspj USsProject* NewProject = FSsLoader::LoadSsProject(InParent, ProjectName, Flags, Buffer, (InBufferEnd - Buffer) + 1); NewProject->SetFilepath( GetCurrentFilename() ); if(NewProject) { if(NewProject->AssetImportData == nullptr) { NewProject->AssetImportData = NewObject<UAssetImportData>(NewProject); } NewProject->AssetImportData->Update(CurrentFilename); FString CurPath = FPaths::GetPath(GetCurrentFilename()); TArray<FString> ImagePaths; TArray<SsTexWrapMode::Type> ImageWrapModes; TArray<SsTexFilterMode::Type> ImageFilterModes; // ssce NewProject->CellmapList.Empty(); NewProject->CellmapList.AddZeroed(NewProject->CellmapNames.Num()); for(int i = 0; i < NewProject->CellmapNames.Num(); ++i) { FString FileName = GetFilePath(CurPath, NewProject->Settings.CellMapBaseDirectory, NewProject->CellmapNames[i].ToString()); TArray<uint8> Data; if(FFileHelper::LoadFileToArray(Data, *FileName)) { const uint8* BufferBegin = Data.GetData(); const uint8* BufferEnd = BufferBegin + Data.Num() - 1; if(FSsLoader::LoadSsCellMap(&(NewProject->CellmapList[i]), BufferBegin, (BufferEnd - BufferBegin) + 1)) { NewProject->CellmapList[i].FileName = NewProject->CellmapNames[i]; if(0 < NewProject->CellmapList[i].ImagePath.Len()) { if(INDEX_NONE == ImagePaths.Find(NewProject->CellmapList[i].ImagePath)) { ImagePaths.Add(NewProject->CellmapList[i].ImagePath); if(NewProject->CellmapList[i].OverrideTexSettings) { ImageWrapModes.Add(NewProject->CellmapList[i].WrapMode); ImageFilterModes.Add(NewProject->CellmapList[i].FilterMode); } else { ImageWrapModes.Add(NewProject->Settings.WrapMode); ImageFilterModes.Add(NewProject->Settings.FilterMode); } } } } } } // ssae NewProject->AnimeList.Empty(); NewProject->AnimeList.AddZeroed(NewProject->AnimepackNames.Num()); for(int i = 0; i < NewProject->AnimepackNames.Num(); ++i) { FString FileName = GetFilePath(CurPath, NewProject->Settings.AnimeBaseDirectory, NewProject->AnimepackNames[i].ToString()); TArray<uint8> Data; if(FFileHelper::LoadFileToArray(Data, *FileName)) { const uint8* BufferBegin = Data.GetData(); const uint8* BufferEnd = BufferBegin + Data.Num() - 1; FSsLoader::LoadSsAnimePack(&(NewProject->AnimeList[i]), BufferBegin, (BufferEnd - BufferBegin) + 1); } } // texture for(int i = 0; i < ImagePaths.Num(); ++i) { FString FileName = GetFilePath(CurPath, NewProject->Settings.ImageBaseDirectory, ImagePaths[i]); UTexture* ImportedTexture = NULL; if(ExistImages && ExistImages->Contains(ImagePaths[i])) { ImportedTexture = ExistImages->FindChecked(ImagePaths[i]); } TArray<uint8> Data; if(FFileHelper::LoadFileToArray(Data, *FileName)) { UTextureFactory* TextureFact = NewObject<UTextureFactory>(); TextureFact->AddToRoot(); FString TextureName = FPaths::GetBaseFilename(ImagePaths[i]); UPackage* TexturePackage = NULL; if(ImportedTexture) { TexturePackage = ImportedTexture->GetOutermost(); } else { FString TexturePackageName; FString BasePackageName = FPackageName::GetLongPackagePath(InParent->GetOutermost()->GetName()) / TextureName; AssetToolsModule.Get().CreateUniqueAssetName(BasePackageName, TEXT(""), TexturePackageName, TextureName); TexturePackage = CreatePackage(NULL, *TexturePackageName); } const uint8* BufferBegin = Data.GetData(); const uint8* BufferEnd = BufferBegin + Data.Num(); UTexture2D* NewTexture = (UTexture2D*)TextureFact->FactoryCreateBinary( UTexture2D::StaticClass(), TexturePackage, FName(*TextureName), Flags, NULL, *FPaths::GetExtension(ImagePaths[i]), BufferBegin, BufferEnd, Warn ); if(NewTexture) { if(ImportSettings->bOverwriteMipGenSettings) { NewTexture->MipGenSettings = TMGS_NoMipmaps; } if(ImportSettings->bOverwriteTextureGroup) { NewTexture->LODGroup = ImportSettings->TextureGroup; } if(ImportSettings->bOverwriteCompressionSettings) { NewTexture->CompressionSettings = TextureCompressionSettings::TC_EditorIcon; } if(ImportSettings->bOverwriteTilingMethodFromSspj) { switch(ImageWrapModes[i]) { case SsTexWrapMode::Clamp: { NewTexture->AddressX = NewTexture->AddressY = TA_Clamp; } break; case SsTexWrapMode::Repeat: { NewTexture->AddressX = NewTexture->AddressY = TA_Wrap; } break; case SsTexWrapMode::Mirror: { NewTexture->AddressX = NewTexture->AddressY = TA_Mirror; } break; } } if(ImportSettings->bOverwriteNeverStream) { NewTexture->NeverStream = true; } if(ImportSettings->bOverwriteFilterFromSspj) { switch(ImageFilterModes[i]) { case SsTexFilterMode::Nearest: { NewTexture->Filter = TF_Nearest; } break; case SsTexFilterMode::Linear: { NewTexture->Filter = TF_Bilinear; } break; } } NewTexture->UpdateResource(); FAssetRegistryModule::AssetCreated(NewTexture); TexturePackage->SetDirtyFlag(true); TextureFact->RemoveFromRoot(); ImportedTexture = NewTexture; } } if(ImportedTexture) { for(int ii = 0; ii < NewProject->CellmapList.Num(); ++ii) { if(NewProject->CellmapList[ii].ImagePath == ImagePaths[i]) { NewProject->CellmapList[ii].Texture = ImportedTexture; } } } } } // インポート終了 FEditorDelegates::OnAssetPostImport.Broadcast(this, NewProject); return NewProject; }