void FAnimGraphConnectionDrawingPolicy::BuildExecutionRoadmap() { UAnimBlueprint* TargetBP = CastChecked<UAnimBlueprint>(FBlueprintEditorUtils::FindBlueprintForGraphChecked(GraphObj)); UAnimBlueprintGeneratedClass* AnimBlueprintClass = (UAnimBlueprintGeneratedClass*)(*(TargetBP->GeneratedClass)); if (TargetBP->GetObjectBeingDebugged() == NULL) { return; } TMap<UProperty*, UObject*> PropertySourceMap; AnimBlueprintClass->GetDebugData().GenerateReversePropertyMap(/*out*/ PropertySourceMap); FAnimBlueprintDebugData& DebugInfo = AnimBlueprintClass->GetAnimBlueprintDebugData(); for (auto VisitIt = DebugInfo.UpdatedNodesThisFrame.CreateIterator(); VisitIt; ++VisitIt) { const FAnimBlueprintDebugData::FNodeVisit& VisitRecord = *VisitIt; if ((VisitRecord.SourceID >= 0) && (VisitRecord.SourceID < AnimBlueprintClass->AnimNodeProperties.Num()) && (VisitRecord.TargetID >= 0) && (VisitRecord.TargetID < AnimBlueprintClass->AnimNodeProperties.Num())) { if (UAnimGraphNode_Base* SourceNode = Cast<UAnimGraphNode_Base>(PropertySourceMap.FindRef(AnimBlueprintClass->AnimNodeProperties[VisitRecord.SourceID]))) { if (UAnimGraphNode_Base* TargetNode = Cast<UAnimGraphNode_Base>(PropertySourceMap.FindRef(AnimBlueprintClass->AnimNodeProperties[VisitRecord.TargetID]))) { //@TODO: Extend the rendering code to allow using the recorded blend weight instead of faked exec times FExecPairingMap& Predecessors = PredecessorNodes.FindOrAdd((UEdGraphNode*)SourceNode); FTimePair& Timings = Predecessors.FindOrAdd((UEdGraphNode*)TargetNode); Timings.PredExecTime = 0.0; Timings.ThisExecTime = VisitRecord.Weight; } } } } }
void SSingleProperty::ReplaceObjects( const TMap<UObject*, UObject*>& OldToNewObjectMap ) { if( HasValidProperty() ) { TArray<UObject*> NewObjectList; bool bObjectsReplaced = false; // Scan all objects and look for objects which need to be replaced for ( TPropObjectIterator Itor( RootPropertyNode->ObjectIterator() ); Itor; ++Itor ) { UObject* Replacement = OldToNewObjectMap.FindRef( Itor->Get() ); if( Replacement ) { bObjectsReplaced = true; NewObjectList.Add( Replacement ); } else { NewObjectList.Add( Itor->Get() ); } } // if any objects were replaced update the observed objects if( bObjectsReplaced ) { SetObject( NewObjectList[0] ); } } }
AGameplayDebuggerPlayerManager& FGameplayDebuggerModule::GetPlayerManager(UWorld* World) { const int32 PurgeInvalidWorldsSize = 5; if (PlayerManagers.Num() > PurgeInvalidWorldsSize) { for (TMap<TWeakObjectPtr<UWorld>, TWeakObjectPtr<AGameplayDebuggerPlayerManager> >::TIterator It(PlayerManagers); It; ++It) { if (!It.Key().IsValid()) { It.RemoveCurrent(); } else if (!It.Value().IsValid()) { It.RemoveCurrent(); } } } TWeakObjectPtr<AGameplayDebuggerPlayerManager> Manager = PlayerManagers.FindRef(World); AGameplayDebuggerPlayerManager* ManagerOb = Manager.Get(); if (ManagerOb == nullptr) { ManagerOb = World->SpawnActor<AGameplayDebuggerPlayerManager>(); PlayerManagers.Add(World, ManagerOb); } check(ManagerOb); return *ManagerOb; }
bool FChunkManifestGenerator::LoadAssetRegistry(const FString& SandboxPath, const TSet<FName>* PackagesToKeep) { UE_LOG(LogChunkManifestGenerator, Display, TEXT("Loading asset registry.")); // Load generated registry for each platform check(Platforms.Num() == 1); for (auto Platform : Platforms) { /*FString PlatformSandboxPath = SandboxPath.Replace(TEXT("[Platform]"), *Platform->PlatformName()); FArchive* AssetRegistryReader = IFileManager::Get().CreateFileReader(*PlatformSandboxPath);*/ FString PlatformSandboxPath = SandboxPath.Replace(TEXT("[Platform]"), *Platform->PlatformName()); FArrayReader FileContents; if (FFileHelper::LoadFileToArray(FileContents, *PlatformSandboxPath) == false) { continue; } FArchive* AssetRegistryReader = &FileContents; TMap<FName, FAssetData*> SavedAssetRegistryData; TArray<FDependsNode*> DependencyData; if (AssetRegistryReader) { AssetRegistry.LoadRegistryData(*AssetRegistryReader, SavedAssetRegistryData, DependencyData); } for (auto& LoadedAssetData : AssetRegistryData) { if (PackagesToKeep && PackagesToKeep->Contains(LoadedAssetData.PackageName) == false) { continue; } FAssetData* FoundAssetData = SavedAssetRegistryData.FindRef(LoadedAssetData.ObjectPath); if ( FoundAssetData ) { LoadedAssetData.ChunkIDs.Append(FoundAssetData->ChunkIDs); SavedAssetRegistryData.Remove(LoadedAssetData.ObjectPath); delete FoundAssetData; } } for (const auto& SavedAsset : SavedAssetRegistryData) { if (PackagesToKeep && PackagesToKeep->Contains(SavedAsset.Value->PackageName)) { AssetRegistryData.Add(*SavedAsset.Value); } delete SavedAsset.Value; } SavedAssetRegistryData.Empty(); } return true; }
FTextFormatArgumentModifier_PluralForm::FTextFormatArgumentModifier_PluralForm(const ETextPluralType InPluralType, const TMap<FTextFormatString, FTextFormat>& InPluralForms, const int32 InLongestPluralFormStringLen, const bool InDoPluralFormsUseFormatArgs) : PluralType(InPluralType) , LongestPluralFormStringLen(InLongestPluralFormStringLen) , bDoPluralFormsUseFormatArgs(InDoPluralFormsUseFormatArgs) { static const FTextFormatString ZeroString = FTextFormatString::MakeReference(TEXT("zero")); static const FTextFormatString OneString = FTextFormatString::MakeReference(TEXT("one")); static const FTextFormatString TwoString = FTextFormatString::MakeReference(TEXT("two")); static const FTextFormatString FewString = FTextFormatString::MakeReference(TEXT("few")); static const FTextFormatString ManyString = FTextFormatString::MakeReference(TEXT("many")); static const FTextFormatString OtherString = FTextFormatString::MakeReference(TEXT("other")); CompiledPluralForms[(int32)ETextPluralForm::Zero] = InPluralForms.FindRef(ZeroString); CompiledPluralForms[(int32)ETextPluralForm::One] = InPluralForms.FindRef(OneString); CompiledPluralForms[(int32)ETextPluralForm::Two] = InPluralForms.FindRef(TwoString); CompiledPluralForms[(int32)ETextPluralForm::Few] = InPluralForms.FindRef(FewString); CompiledPluralForms[(int32)ETextPluralForm::Many] = InPluralForms.FindRef(ManyString); CompiledPluralForms[(int32)ETextPluralForm::Other] = InPluralForms.FindRef(OtherString); }
void UActorComponent::PostEditUndo() { // Objects marked pending kill don't call PostEditChange() from UObject::PostEditUndo(), // so they can leave an EditReregisterContexts entry around if they are deleted by an undo action. if( IsPendingKill() ) { // The reregister context won't bother attaching components that are 'pending kill'. FComponentReregisterContext* ReregisterContext = EditReregisterContexts.FindRef(this); if(ReregisterContext) { delete ReregisterContext; EditReregisterContexts.Remove(this); } } else { Owner = GetTypedOuter<AActor>(); bCanUseCachedOwner = true; // Let the component be properly registered, after it was restored. if (Owner) { Owner->AddOwnedComponent(this); } TArray<UObject*> Children; GetObjectsWithOuter(this, Children); for (UObject* Child : Children) { if (UActorComponent* ChildComponent = Cast<UActorComponent>(Child)) { if (ChildComponent->Owner) { ChildComponent->Owner->RemoveOwnedComponent(ChildComponent); } ChildComponent->Owner = Owner; if (Owner) { Owner->AddOwnedComponent(ChildComponent); } } } if (GetWorld()) { GetWorld()->UpdateActorComponentEndOfFrameUpdateState(this); } } Super::PostEditUndo(); }
void FMultiBox::ApplyCustomizedBlocks() { if( IsCustomizable() ) { CustomizationData->LoadCustomizedBlocks(); // Build a map of commands to existing blocks, we'll try to use existing blocks before creating new ones TMap< TSharedPtr<const FUICommandInfo>, TSharedPtr<const FMultiBlock> > CommandToBlockMap; for( int32 BlockIndex = 0; BlockIndex < Blocks.Num(); ++BlockIndex ) { TSharedPtr<const FMultiBlock> Block = Blocks[BlockIndex]; if( Block->GetAction().IsValid() ) { CommandToBlockMap.Add( Block->GetAction(), Block ); } } // Rebuild the users customized box by executing the transactions the user made to get the // box to its customized state for( uint32 TransIndex = 0; TransIndex < CustomizationData->GetNumTransactions(); ++TransIndex ) { const FCustomBlockTransaction& Transaction = CustomizationData->GetTransaction(TransIndex); // Try and find the block in the default map; TSharedPtr<const FMultiBlock> Block = CommandToBlockMap.FindRef( Transaction.Command.Pin() ); if( Transaction.TransactionType == FCustomBlockTransaction::Add ) { if( !Block.IsValid() ) { Block = MakeMultiBlockFromCommand( Transaction.Command.Pin(), false ); } if( Block.IsValid() ) { Blocks.Insert( Block.ToSharedRef(), FMath::Clamp( Transaction.BlockIndex, 0, Blocks.Num() ) ); } } else { if( Block.IsValid() ) { Blocks.Remove( Block.ToSharedRef() ); } } } } }
void UActorComponent::PostEditUndo() { // Objects marked pending kill don't call PostEditChange() from UObject::PostEditUndo(), // so they can leave an EditReregisterContexts entry around if they are deleted by an undo action. if( IsPendingKill() ) { // The reregister context won't bother attaching components that are 'pending kill'. FComponentReregisterContext* ReregisterContext = EditReregisterContexts.FindRef(this); if(ReregisterContext) { delete ReregisterContext; EditReregisterContexts.Remove(this); } } Super::PostEditUndo(); }
void PostRemovedOldPin(FOptionalPinFromProperty& Record, int32 ArrayIndex, UProperty* Property, uint8* PropertyAddress) const override { check(PropertyAddress != NULL); check(!Record.bShowPin); if (Record.bCanToggleVisibility && (OldPins != NULL)) { const FString OldPinName = (ArrayIndex != INDEX_NONE) ? FString::Printf(TEXT("%s_%d"), *(Record.PropertyName.ToString()), ArrayIndex) : Record.PropertyName.ToString(); if (UEdGraphPin* OldPin = OldPinMap.FindRef(OldPinName)) { // Pin was visible but it's now hidden // Convert DefaultValue/DefaultValueObject and push back into the struct FBlueprintEditorUtils::ImportKismetDefaultValueToProperty(OldPin, Property, PropertyAddress, BaseNode); } } }
bool FDataScannerImpl::FindExistingChunk(const TMap<uint64, TSet<FGuid>>& ChunkLookup, TMap<FGuid, FSHAHash>& ChunkShaHashes, uint64 ChunkHash, const FRollingHash<WindowSize>& RollingHash, FGuid& OutMatchedChunk) { FStatsScopedTimer FindTimer(StatFindMatchTime); bool bFoundChunkMatch = false; if (ChunkLookup.Contains(ChunkHash)) { FSHAHash ChunkSha; RollingHash.GetWindowData().GetShaHash(ChunkSha); for (FGuid& PotentialMatch : ChunkLookup.FindRef(ChunkHash)) { // Use sha if we have it if (ChunkShaHashes.Contains(PotentialMatch)) { if(ChunkSha == ChunkShaHashes[PotentialMatch]) { bFoundChunkMatch = true; OutMatchedChunk = PotentialMatch; break; } } else { // Otherwise compare data TArray<uint8> SerialBuffer; FStatsScopedTimer DataMatchTimer(StatDataMatchTime); FStatsCollector::Accumulate(StatChunkDataChecks, 1); SerialBuffer.AddUninitialized(WindowSize); RollingHash.GetWindowData().Serialize(SerialBuffer.GetData()); bool ChunkFound = false; if (DataMatcher->CompareData(PotentialMatch, ChunkHash, SerialBuffer, ChunkFound)) { FStatsCollector::Accumulate(StatChunkDataMatches, 1); ChunkShaHashes.Add(PotentialMatch, ChunkSha); bFoundChunkMatch = true; OutMatchedChunk = PotentialMatch; break; } else if(!ChunkFound) { FStatsCollector::Accumulate(StatMissingChunks, 1); } } FStatsCollector::Accumulate(StatHashCollisions, 1); } } return bFoundChunkMatch; }
FName FNativeClassHierarchy::GetClassPathRootForModule(const FName& InModuleName, const TSet<FName>& InGameModules, const TMap<FName, FName>& InPluginModules) { static const FName EngineRootNodeName = "Classes_Engine"; static const FName GameRootNodeName = "Classes_Game"; // Work out which root this class should go under (anything that isn't a game or plugin module goes under engine) FName RootNodeName = EngineRootNodeName; if(InGameModules.Contains(InModuleName)) { RootNodeName = GameRootNodeName; } else if(InPluginModules.Contains(InModuleName)) { const FName PluginName = InPluginModules.FindRef(InModuleName); RootNodeName = FName(*(FString(TEXT("Classes_")) + PluginName.ToString())); } return RootNodeName; }
void SDetailsView::ReplaceObjects( const TMap<UObject*, UObject*>& OldToNewObjectMap ) { TArray< TWeakObjectPtr< UObject > > NewObjectList; bool bObjectsReplaced = false; TArray< FObjectPropertyNode* > ObjectNodes; for(TSharedPtr<FComplexPropertyNode>& RootNode : RootPropertyNodes) { PropertyEditorHelpers::CollectObjectNodes(RootNode, ObjectNodes ); } for( int32 ObjectNodeIndex = 0; ObjectNodeIndex < ObjectNodes.Num(); ++ObjectNodeIndex ) { FObjectPropertyNode* CurrentNode = ObjectNodes[ObjectNodeIndex]; // Scan all objects and look for objects which need to be replaced for ( TPropObjectIterator Itor( CurrentNode->ObjectIterator() ); Itor; ++Itor ) { UObject* Replacement = OldToNewObjectMap.FindRef( Itor->Get() ); if( Replacement && Replacement->GetClass() == Itor->Get()->GetClass() ) { bObjectsReplaced = true; if( CurrentNode->IsRootNode() ) { // Note: only root objects count for the new object list. Sub-Objects (i.e components count as needing to be replaced but they don't belong in the top level object list NewObjectList.Add( Replacement ); } } else if( CurrentNode->IsRootNode() ) { // Note: only root objects count for the new object list. Sub-Objects (i.e components count as needing to be replaced but they don't belong in the top level object list NewObjectList.Add( Itor->Get() ); } } } if( bObjectsReplaced ) { SetObjectArrayPrivate( NewObjectList ); } }
void UActorComponent::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) { FComponentReregisterContext* ReregisterContext = EditReregisterContexts.FindRef(this); if(ReregisterContext) { delete ReregisterContext; EditReregisterContexts.Remove(this); } // The component or its outer could be pending kill when calling PostEditChange when applying a transaction. // Don't do do a full recreate in this situation, and instead simply detach. if( IsPendingKill() ) { // @todo UE4 james should this call UnregsiterComponent instead to remove itself from the RegisteteredComponents array on the owner? ExecuteUnregisterEvents(); World = NULL; } Super::PostEditChangeProperty(PropertyChangedEvent); }
void UActorComponent::ConsolidatedPostEditChange(const FPropertyChangedEvent& PropertyChangedEvent) { FComponentReregisterContext* ReregisterContext = EditReregisterContexts.FindRef(this); if(ReregisterContext) { delete ReregisterContext; EditReregisterContexts.Remove(this); AActor* MyOwner = GetOwner(); if ( MyOwner && !MyOwner->IsTemplate() && PropertyChangedEvent.ChangeType != EPropertyChangeType::Interactive ) { MyOwner->RerunConstructionScripts(); } } // The component or its outer could be pending kill when calling PostEditChange when applying a transaction. // Don't do do a full recreate in this situation, and instead simply detach. if( IsPendingKill() ) { // @todo UE4 james should this call UnregisterComponent instead to remove itself from the RegisteredComponents array on the owner? ExecuteUnregisterEvents(); World = NULL; } }
void FActorReplacementHelper::AttachChildActors(USceneComponent* RootComponent, const TMap<UObject*, UObject*>& OldToNewInstanceMap) { // if we had attached children reattach them now - unless they are already attached for (FAttachedActorInfo& Info : PendingChildAttachments) { // Check for a reinstanced attachment, and redirect to the new instance if found AActor* NewAttachedActor = Cast<AActor>(OldToNewInstanceMap.FindRef(Info.AttachedActor)); if (NewAttachedActor) { Info.AttachedActor = NewAttachedActor; } // If this actor is no longer attached to anything, reattach if (!Info.AttachedActor->IsPendingKill() && Info.AttachedActor->GetAttachParentActor() == nullptr) { USceneComponent* ChildRoot = Info.AttachedActor->GetRootComponent(); if (ChildRoot && ChildRoot->AttachParent != RootComponent) { ChildRoot->AttachTo(RootComponent, Info.AttachedToSocket, EAttachLocation::KeepWorldPosition); ChildRoot->UpdateComponentToWorld(); } } } }
void PostInitNewPin(UEdGraphPin* Pin, FOptionalPinFromProperty& Record, int32 ArrayIndex, UProperty* Property, uint8* PropertyAddress) const override { check(PropertyAddress != NULL); check(Record.bShowPin); if (OldPins == NULL) { // Initial construction of a visible pin; copy values from the struct FBlueprintEditorUtils::ExportPropertyToKismetDefaultValue(Pin, Property, PropertyAddress, BaseNode); } else if (Record.bCanToggleVisibility) { if (UEdGraphPin* OldPin = OldPinMap.FindRef(Pin->PinName)) { // Was already visible } else { // Showing a pin that was previously hidden, during a reconstruction // Convert the struct property into DefaultValue/DefaultValueObject FBlueprintEditorUtils::ExportPropertyToKismetDefaultValue(Pin, Property, PropertyAddress, BaseNode); } } }
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(); }
void FBlueprintCompileReinstancer::ReplaceInstancesOfClass(UClass* OldClass, UClass* NewClass, UObject* OriginalCDO, TSet<UObject*>* ObjectsThatShouldUseOldStuff) { USelection* SelectedActors; bool bSelectionChanged = false; TArray<UObject*> ObjectsToReplace; const bool bLogConversions = false; // for debugging // Map of old objects to new objects TMap<UObject*, UObject*> OldToNewInstanceMap; TMap<UClass*, UClass*> OldToNewClassMap; OldToNewClassMap.Add(OldClass, NewClass); TMap<FStringAssetReference, UObject*> ReinstancedObjectsWeakReferenceMap; // actors being replace TArray<FActorReplacementHelper> ReplacementActors; // A list of objects (e.g. Blueprints) that potentially have editors open that we need to refresh TArray<UObject*> PotentialEditorsForRefreshing; // A list of component owners that need their construction scripts re-ran (because a component of theirs has been reinstanced) TSet<AActor*> OwnersToReconstruct; // Set global flag to let system know we are reconstructing blueprint instances TGuardValue<bool> GuardTemplateNameFlag(GIsReconstructingBlueprintInstances, true); struct FObjectRemappingHelper { void OnObjectsReplaced(const TMap<UObject*, UObject*>& InReplacedObjects) { ReplacedObjects.Append(InReplacedObjects); } TMap<UObject*, UObject*> ReplacedObjects; } ObjectRemappingHelper; FDelegateHandle OnObjectsReplacedHandle = GEditor->OnObjectsReplaced().AddRaw(&ObjectRemappingHelper,&FObjectRemappingHelper::OnObjectsReplaced); { BP_SCOPED_COMPILER_EVENT_STAT(EKismetReinstancerStats_ReplaceInstancesOfClass); const bool bIncludeDerivedClasses = false; GetObjectsOfClass(OldClass, ObjectsToReplace, bIncludeDerivedClasses); SelectedActors = GEditor->GetSelectedActors(); SelectedActors->BeginBatchSelectOperation(); SelectedActors->Modify(); // Then fix 'real' (non archetype) instances of the class for (UObject* OldObject : ObjectsToReplace) { // Skip non-archetype instances, EXCEPT for component templates const bool bIsComponent = NewClass->IsChildOf(UActorComponent::StaticClass()); if ((!bIsComponent && OldObject->IsTemplate()) || OldObject->IsPendingKill()) { continue; } UBlueprint* CorrespondingBlueprint = Cast<UBlueprint>(OldObject->GetClass()->ClassGeneratedBy); UObject* OldBlueprintDebugObject = nullptr; // If this object is being debugged, cache it off so we can preserve the 'object being debugged' association if ((CorrespondingBlueprint != nullptr) && (CorrespondingBlueprint->GetObjectBeingDebugged() == OldObject)) { OldBlueprintDebugObject = OldObject; } AActor* OldActor = Cast<AActor>(OldObject); UObject* NewUObject = nullptr; // if the object to replace is an actor... if (OldActor != nullptr) { FVector Location = FVector::ZeroVector; FRotator Rotation = FRotator::ZeroRotator; if (USceneComponent* OldRootComponent = OldActor->GetRootComponent()) { Location = OldActor->GetActorLocation(); Rotation = OldActor->GetActorRotation(); } // If this actor was spawned from an Archetype, we spawn the new actor from the new version of that archetype UObject* OldArchetype = OldActor->GetArchetype(); UWorld* World = OldActor->GetWorld(); AActor* NewArchetype = Cast<AActor>(OldToNewInstanceMap.FindRef(OldArchetype)); // Check that either this was an instance of the class directly, or we found a new archetype for it check(OldArchetype == OldClass->GetDefaultObject() || NewArchetype); // Spawn the new actor instance, in the same level as the original, but deferring running the construction script until we have transferred modified properties ULevel* ActorLevel = OldActor->GetLevel(); UClass** MappedClass = OldToNewClassMap.Find(OldActor->GetClass()); UClass* SpawnClass = MappedClass ? *MappedClass : NewClass; FActorSpawnParameters SpawnInfo; SpawnInfo.OverrideLevel = ActorLevel; SpawnInfo.Template = NewArchetype; SpawnInfo.bNoCollisionFail = true; SpawnInfo.bDeferConstruction = true; // Temporarily remove the deprecated flag so we can respawn the Blueprint in the level const bool bIsClassDeprecated = SpawnClass->HasAnyClassFlags(CLASS_Deprecated); SpawnClass->ClassFlags &= ~CLASS_Deprecated; AActor* NewActor = World->SpawnActor(SpawnClass, &Location, &Rotation, SpawnInfo); // Reassign the deprecated flag if it was previously assigned if (bIsClassDeprecated) { SpawnClass->ClassFlags |= CLASS_Deprecated; } check(NewActor != nullptr); NewUObject = NewActor; // store the new actor for the second pass (NOTE: this detaches // OldActor from all child/parent attachments) // // running the NewActor's construction-script is saved for that // second pass (because the construction-script may reference // another instance that hasn't been replaced yet). ReplacementActors.Add(FActorReplacementHelper(NewActor, OldActor)); ReinstancedObjectsWeakReferenceMap.Add(OldObject, NewUObject); OldActor->DestroyConstructedComponents(); // don't want to serialize components from the old actor // Unregister native components so we don't copy any sub-components they generate for themselves (like UCameraComponent does) OldActor->UnregisterAllComponents(); // Unregister any native components, might have cached state based on properties we are going to overwrite NewActor->UnregisterAllComponents(); UEditorEngine::CopyPropertiesForUnrelatedObjects(OldActor, NewActor); // reset properties/streams NewActor->ResetPropertiesForConstruction(); // register native components NewActor->RegisterAllComponents(); // // clean up the old actor (unselect it, remove it from the world, etc.)... if (OldActor->IsSelected()) { GEditor->SelectActor(OldActor, /*bInSelected =*/false, /*bNotify =*/false); bSelectionChanged = true; } if (GEditor->Layers.IsValid()) // ensure(NULL != GEditor->Layers) ?? While cooking the Layers is NULL. { GEditor->Layers->DisassociateActorFromLayers(OldActor); } World->EditorDestroyActor(OldActor, /*bShouldModifyLevel =*/true); OldToNewInstanceMap.Add(OldActor, NewActor); } else { FName OldName(OldObject->GetFName()); OldObject->Rename(NULL, OldObject->GetOuter(), REN_DoNotDirty | REN_DontCreateRedirectors); NewUObject = NewObject<UObject>(OldObject->GetOuter(), NewClass, OldName); check(NewUObject != nullptr); UEditorEngine::CopyPropertiesForUnrelatedObjects(OldObject, NewUObject); if (UAnimInstance* AnimTree = Cast<UAnimInstance>(NewUObject)) { // Initialising the anim instance isn't enough to correctly set up the skeletal mesh again in a // paused world, need to initialise the skeletal mesh component that contains the anim instance. if (USkeletalMeshComponent* SkelComponent = Cast<USkeletalMeshComponent>(AnimTree->GetOuter())) { SkelComponent->InitAnim(true); } } OldObject->RemoveFromRoot(); OldObject->MarkPendingKill(); OldToNewInstanceMap.Add(OldObject, NewUObject); if (bIsComponent) { UActorComponent* Component = Cast<UActorComponent>(NewUObject); AActor* OwningActor = Component->GetOwner(); if (OwningActor) { OwningActor->ResetOwnedComponents(); // Check to see if they have an editor that potentially needs to be refreshed if (OwningActor->GetClass()->ClassGeneratedBy) { PotentialEditorsForRefreshing.AddUnique(OwningActor->GetClass()->ClassGeneratedBy); } // we need to keep track of actor instances that need // their construction scripts re-ran (since we've just // replaced a component they own) OwnersToReconstruct.Add(OwningActor); } } } // If this original object came from a blueprint and it was in the selected debug set, change the debugging to the new object. if ((CorrespondingBlueprint) && (OldBlueprintDebugObject) && (NewUObject)) { CorrespondingBlueprint->SetObjectBeingDebugged(NewUObject); } if (bLogConversions) { UE_LOG(LogBlueprint, Log, TEXT("Converted instance '%s' to '%s'"), *OldObject->GetPathName(), *NewUObject->GetPathName()); } } } GEditor->OnObjectsReplaced().Remove(OnObjectsReplacedHandle); // Now replace any pointers to the old archetypes/instances with pointers to the new one TArray<UObject*> SourceObjects; TArray<UObject*> DstObjects; OldToNewInstanceMap.GenerateKeyArray(SourceObjects); OldToNewInstanceMap.GenerateValueArray(DstObjects); // Also look for references in new spawned objects. SourceObjects.Append(DstObjects); FReplaceReferenceHelper::IncludeCDO(OldClass, NewClass, OldToNewInstanceMap, SourceObjects, OriginalCDO); FReplaceReferenceHelper::FindAndReplaceReferences(SourceObjects, ObjectsThatShouldUseOldStuff, ObjectsToReplace, OldToNewInstanceMap, ReinstancedObjectsWeakReferenceMap); { BP_SCOPED_COMPILER_EVENT_STAT(EKismetReinstancerStats_ReplacementConstruction); // the process of setting up new replacement actors is split into two // steps (this here, is the second)... // // the "finalization" here runs the replacement actor's construction- // script and is left until late to account for a scenario where the // construction-script attempts to modify another instance of the // same class... if this were to happen above, in the ObjectsToReplace // loop, then accessing that other instance would cause an assert in // UProperty::ContainerPtrToValuePtrInternal() (which appropriatly // complains that the other instance's type doesn't match because it // hasn't been replaced yet... that's why we wait until after // FArchiveReplaceObjectRef to run construction-scripts). for (FActorReplacementHelper& ReplacementActor : ReplacementActors) { ReplacementActor.Finalize(ObjectRemappingHelper.ReplacedObjects); } } SelectedActors->EndBatchSelectOperation(); if (bSelectionChanged) { GEditor->NoteSelectionChange(); } if (GEditor) { // Refresh any editors for objects that we've updated components for for (auto BlueprintAsset : PotentialEditorsForRefreshing) { FBlueprintEditor* BlueprintEditor = static_cast<FBlueprintEditor*>(FAssetEditorManager::Get().FindEditorForAsset(BlueprintAsset, /*bFocusIfOpen =*/false)); if (BlueprintEditor) { BlueprintEditor->RefreshEditors(); } } } // in the case where we're replacing component instances, we need to make // sure to re-run their owner's construction scripts for (AActor* ActorInstance : OwnersToReconstruct) { ActorInstance->RerunConstructionScripts(); } }
void FManifestBuilderImpl::BuildManifest() { TMap<FGuid, FChunkInfo> ChunkInfoLookup; bool Running = true; while (Running) { FDataScannerPtr NextScanner = GetNextScanner(); if (NextScanner.IsValid()) { FDataScanResult ScanResult = NextScanner->GetResultWhenComplete(); ChunkInfoLookup.Append(ScanResult.ChunkInfo); // Always reverse for now if (ScanResult.DataStructure.Num() > 0) { FChunkPart& ChunkPart = ScanResult.DataStructure[0]; if (ChunkPart.DataOffset != FileBuilder.CurrentDataPos) { check(ChunkPart.DataOffset < FileBuilder.CurrentDataPos); // Missing data! bool FoundPosition = false; uint64 DataCount = 0; for (int32 FileIdx = 0; FileIdx < Manifest->Data->FileManifestList.Num() && !FoundPosition; ++FileIdx) { FFileManifestData& FileManifest = Manifest->Data->FileManifestList[FileIdx]; FileManifest.Init(); uint64 FileStartIdx = DataCount; uint64 FileEndIdx = FileStartIdx + FileManifest.GetFileSize(); if (FileEndIdx > ChunkPart.DataOffset) { for (int32 ChunkIdx = 0; ChunkIdx < FileManifest.FileChunkParts.Num() && !FoundPosition; ++ChunkIdx) { FChunkPartData& ChunkPartData = FileManifest.FileChunkParts[ChunkIdx]; uint64 ChunkPartEndIdx = DataCount + ChunkPartData.Size; if (ChunkPartEndIdx < ChunkPart.DataOffset) { DataCount += ChunkPartData.Size; } else if (ChunkPartEndIdx > ChunkPart.DataOffset) { ChunkPartData.Size = ChunkPart.DataOffset - DataCount; FileBuilder.CurrentDataPos = DataCount + ChunkPartData.Size; FileManifest.FileChunkParts.SetNum(ChunkIdx + 1, false); FileManifest.FileChunkParts.Emplace(); Manifest->Data->FileManifestList.SetNum(FileIdx + 1, false); FileBuilder.FileManifest = &Manifest->Data->FileManifestList.Last(); bool FoundFile = BuildStreamer->GetFileSpan(FileStartIdx, FileBuilder.FileSpan); check(FoundFile); // Incorrect positional tracking FoundPosition = true; } else { FileBuilder.CurrentDataPos = DataCount + ChunkPartData.Size; FileManifest.FileChunkParts.SetNum(ChunkIdx + 1, false); FileManifest.FileChunkParts.Emplace(); Manifest->Data->FileManifestList.SetNum(FileIdx + 1, false); FileBuilder.FileManifest = &Manifest->Data->FileManifestList.Last(); bool FoundFile = BuildStreamer->GetFileSpan(FileStartIdx, FileBuilder.FileSpan); check(FoundFile); // Incorrect positional tracking FoundPosition = true; } } } else if (FileEndIdx < ChunkPart.DataOffset) { DataCount += FileManifest.GetFileSize(); } else { FileBuilder.FileManifest = nullptr; FileBuilder.CurrentDataPos = DataCount + FileManifest.GetFileSize(); Manifest->Data->FileManifestList.SetNum(FileIdx + 1, false); FoundPosition = true; } } check(ChunkPart.DataOffset == FileBuilder.CurrentDataPos); check(FileBuilder.FileManifest == nullptr || FileBuilder.FileSpan.Filename == Manifest->Data->FileManifestList.Last().Filename); } } for (int32 idx = 0; idx < ScanResult.DataStructure.Num(); ++idx) { FChunkPart& ChunkPart = ScanResult.DataStructure[idx]; // Starting new file? if (FileBuilder.FileManifest == nullptr) { Manifest->Data->FileManifestList.Emplace(); FileBuilder.FileManifest = &Manifest->Data->FileManifestList.Last(); bool FoundFile = BuildStreamer->GetFileSpan(FileBuilder.CurrentDataPos, FileBuilder.FileSpan); check(FoundFile); // Incorrect positional tracking FileBuilder.FileManifest->Filename = FileBuilder.FileSpan.Filename; FileBuilder.FileManifest->FileChunkParts.Emplace(); } FChunkPartData& FileChunkPartData = FileBuilder.FileManifest->FileChunkParts.Last(); FileChunkPartData.Guid = ChunkPart.ChunkGuid; FileChunkPartData.Offset = (FileBuilder.CurrentDataPos - ChunkPart.DataOffset) + ChunkPart.ChunkOffset; // Process data into file manifests int64 FileDataLeft = (FileBuilder.FileSpan.StartIdx + FileBuilder.FileSpan.Size) - FileBuilder.CurrentDataPos; int64 ChunkDataLeft = (ChunkPart.DataOffset + ChunkPart.PartSize) - FileBuilder.CurrentDataPos; check(FileDataLeft > 0); check(ChunkDataLeft > 0); if (ChunkDataLeft >= FileDataLeft) { FileBuilder.CurrentDataPos += FileDataLeft; FileChunkPartData.Size = FileDataLeft; } else { FileBuilder.CurrentDataPos += ChunkDataLeft; FileChunkPartData.Size = ChunkDataLeft; } FileDataLeft = (FileBuilder.FileSpan.StartIdx + FileBuilder.FileSpan.Size) - FileBuilder.CurrentDataPos; ChunkDataLeft = (ChunkPart.DataOffset + ChunkPart.PartSize) - FileBuilder.CurrentDataPos; check(FileDataLeft == 0 || ChunkDataLeft == 0); // End of file? if (FileDataLeft == 0) { // Fill out rest of data?? FFileSpan FileSpan; bool FoundFile = BuildStreamer->GetFileSpan(FileBuilder.FileSpan.StartIdx, FileSpan); check(FoundFile); // Incorrect positional tracking check(FileSpan.Filename == FileBuilder.FileManifest->Filename); FMemory::Memcpy(FileBuilder.FileManifest->FileHash.Hash, FileSpan.SHAHash.Hash, FSHA1::DigestSize); FFileAttributes Attributes = FileAttributesMap.FindRef(FileSpan.Filename); FileBuilder.FileManifest->bIsUnixExecutable = Attributes.bUnixExecutable || FileSpan.IsUnixExecutable; FileBuilder.FileManifest->SymlinkTarget = FileSpan.SymlinkTarget; FileBuilder.FileManifest->bIsReadOnly = Attributes.bReadOnly; FileBuilder.FileManifest->bIsCompressed = Attributes.bCompressed; FileBuilder.FileManifest->InstallTags = Attributes.InstallTags.Array(); FileBuilder.FileManifest->Init(); check(FileBuilder.FileManifest->GetFileSize() == FileBuilder.FileSpan.Size); FileBuilder.FileManifest = nullptr; } else if (ChunkDataLeft == 0) { FileBuilder.FileManifest->FileChunkParts.Emplace(); } // Continue with this chunk? if (ChunkDataLeft > 0) { --idx; } } } else { if (EndOfData) { Running = false; } else { CheckForWork->Wait(); CheckForWork->Reset(); } } } // Fill out chunk list from only chunks that remain referenced TSet<FGuid> ReferencedChunks; for (const auto& FileManifest : Manifest->Data->FileManifestList) { for (const auto& ChunkPart : FileManifest.FileChunkParts) { if (ReferencedChunks.Contains(ChunkPart.Guid) == false) { auto& ChunkInfo = ChunkInfoLookup[ChunkPart.Guid]; ReferencedChunks.Add(ChunkPart.Guid); Manifest->Data->ChunkList.Emplace(); auto& ChunkInfoData = Manifest->Data->ChunkList.Last(); ChunkInfoData.Guid = ChunkPart.Guid; ChunkInfoData.Hash = ChunkInfo.Hash; FMemory::Memcpy(ChunkInfoData.ShaHash.Hash, ChunkInfo.ShaHash.Hash, FSHA1::DigestSize); ChunkInfoData.FileSize = ChunkInfo.ChunkFileSize; ChunkInfoData.GroupNumber = FCrc::MemCrc32(&ChunkPart.Guid, sizeof(FGuid)) % 100; } } } // Get empty files FSHA1 EmptyHasher; EmptyHasher.Final(); const TArray< FString >& EmptyFileList = BuildStreamer->GetEmptyFiles(); for (const auto& EmptyFile : EmptyFileList) { Manifest->Data->FileManifestList.Emplace(); FFileManifestData& EmptyFileManifest = Manifest->Data->FileManifestList.Last(); EmptyFileManifest.Filename = EmptyFile; EmptyHasher.GetHash(EmptyFileManifest.FileHash.Hash); } // Fill out lookups Manifest->InitLookups(); }
void UOffAxisGameViewportClient::Draw(FViewport* InViewport, FCanvas* SceneCanvas) { //Valid SceneCanvas is required. Make this explicit. check(SceneCanvas); FCanvas* DebugCanvas = InViewport->GetDebugCanvas(); // Create a temporary canvas if there isn't already one. static FName CanvasObjectName(TEXT("CanvasObject")); UCanvas* CanvasObject = GetCanvasByName(CanvasObjectName); CanvasObject->Canvas = SceneCanvas; // Create temp debug canvas object static FName DebugCanvasObjectName(TEXT("DebugCanvasObject")); UCanvas* DebugCanvasObject = GetCanvasByName(DebugCanvasObjectName); DebugCanvasObject->Canvas = DebugCanvas; DebugCanvasObject->Init(InViewport->GetSizeXY().X, InViewport->GetSizeXY().Y, NULL); const bool bScaledToRenderTarget = GEngine->HMDDevice.IsValid() && GEngine->IsStereoscopic3D(InViewport); if (bScaledToRenderTarget) { // Allow HMD to modify screen settings GEngine->HMDDevice->UpdateScreenSettings(Viewport); } if (DebugCanvas) { DebugCanvas->SetScaledToRenderTarget(bScaledToRenderTarget); } if (SceneCanvas) { SceneCanvas->SetScaledToRenderTarget(bScaledToRenderTarget); } bool bUIDisableWorldRendering = false; FViewElementDrawer GameViewDrawer; // create the view family for rendering the world scene to the viewport's render target FSceneViewFamilyContext ViewFamily(FSceneViewFamily::ConstructionValues( InViewport, GetWorld()->Scene, EngineShowFlags) .SetRealtimeUpdate(true)); // Allow HMD to modify the view later, just before rendering if (GEngine->HMDDevice.IsValid() && GEngine->IsStereoscopic3D(InViewport)) { ISceneViewExtension* HmdViewExt = GEngine->HMDDevice->GetViewExtension(); if (HmdViewExt) { ViewFamily.ViewExtensions.Add(HmdViewExt); HmdViewExt->ModifyShowFlags(ViewFamily.EngineShowFlags); } } ESplitScreenType::Type SplitScreenConfig = GetCurrentSplitscreenConfiguration(); EngineShowFlagOverride(ESFIM_Game, (EViewModeIndex)ViewModeIndex, ViewFamily.EngineShowFlags, NAME_None, SplitScreenConfig != ESplitScreenType::None); TMap<ULocalPlayer*, FSceneView*> PlayerViewMap; FAudioDevice* AudioDevice = GEngine->GetAudioDevice(); bool bReverbSettingsFound = false; FReverbSettings ReverbSettings; class AAudioVolume* AudioVolume = nullptr; for (FConstPlayerControllerIterator Iterator = GetWorld()->GetPlayerControllerIterator(); Iterator; ++Iterator) { APlayerController* PlayerController = *Iterator; if (PlayerController) { ULocalPlayer* LocalPlayer = Cast<ULocalPlayer>(PlayerController->Player); if (LocalPlayer) { const bool bEnableStereo = GEngine->IsStereoscopic3D(InViewport); int32 NumViews = bEnableStereo ? 2 : 1; for (int i = 0; i < NumViews; ++i) { // Calculate the player's view information. FVector ViewLocation; FRotator ViewRotation; EStereoscopicPass PassType = !bEnableStereo ? eSSP_FULL : ((i == 0) ? eSSP_LEFT_EYE : eSSP_RIGHT_EYE); FSceneView* View = LocalPlayer->CalcSceneView(&ViewFamily, ViewLocation, ViewRotation, InViewport, &GameViewDrawer, PassType); if (mOffAxisMatrixSetted) UpdateProjectionMatrix(View, mOffAxisMatrix); if (View) { if (View->Family->EngineShowFlags.Wireframe) { // Wireframe color is emissive-only, and mesh-modifying materials do not use material substitution, hence... View->DiffuseOverrideParameter = FVector4(0.f, 0.f, 0.f, 0.f); View->SpecularOverrideParameter = FVector4(0.f, 0.f, 0.f, 0.f); } else if (View->Family->EngineShowFlags.OverrideDiffuseAndSpecular) { View->DiffuseOverrideParameter = FVector4(GEngine->LightingOnlyBrightness.R, GEngine->LightingOnlyBrightness.G, GEngine->LightingOnlyBrightness.B, 0.0f); View->SpecularOverrideParameter = FVector4(.1f, .1f, .1f, 0.0f); } else if (View->Family->EngineShowFlags.ReflectionOverride) { View->DiffuseOverrideParameter = FVector4(0.f, 0.f, 0.f, 0.f); View->SpecularOverrideParameter = FVector4(1, 1, 1, 0.0f); View->NormalOverrideParameter = FVector4(0, 0, 1, 0.0f); View->RoughnessOverrideParameter = FVector2D(0.0f, 0.0f); } if (!View->Family->EngineShowFlags.Diffuse) { View->DiffuseOverrideParameter = FVector4(0.f, 0.f, 0.f, 0.f); } if (!View->Family->EngineShowFlags.Specular) { View->SpecularOverrideParameter = FVector4(0.f, 0.f, 0.f, 0.f); } View->CameraConstrainedViewRect = View->UnscaledViewRect; // If this is the primary drawing pass, update things that depend on the view location if (i == 0) { // Save the location of the view. LocalPlayer->LastViewLocation = ViewLocation; PlayerViewMap.Add(LocalPlayer, View); // Update the listener. if (AudioDevice != NULL) { FVector Location; FVector ProjFront; FVector ProjRight; PlayerController->GetAudioListenerPosition(/*out*/ Location, /*out*/ ProjFront, /*out*/ ProjRight); FTransform ListenerTransform(FRotationMatrix::MakeFromXY(ProjFront, ProjRight)); ListenerTransform.SetTranslation(Location); ListenerTransform.NormalizeRotation(); bReverbSettingsFound = true; FReverbSettings PlayerReverbSettings; FInteriorSettings PlayerInteriorSettings; class AAudioVolume* PlayerAudioVolume = GetWorld()->GetAudioSettings(Location, &PlayerReverbSettings, &PlayerInteriorSettings); if (AudioVolume == nullptr || (PlayerAudioVolume != nullptr && PlayerAudioVolume->Priority > AudioVolume->Priority)) { AudioVolume = PlayerAudioVolume; ReverbSettings = PlayerReverbSettings; } uint32 ViewportIndex = PlayerViewMap.Num() - 1; AudioDevice->SetListener(ViewportIndex, ListenerTransform, (View->bCameraCut ? 0.f : GetWorld()->GetDeltaSeconds()), PlayerAudioVolume, PlayerInteriorSettings); } } // Add view information for resource streaming. IStreamingManager::Get().AddViewInformation(View->ViewMatrices.ViewOrigin, View->ViewRect.Width(), View->ViewRect.Width() * View->ViewMatrices.ProjMatrix.M[0][0]); GetWorld()->ViewLocationsRenderedLastFrame.Add(View->ViewMatrices.ViewOrigin); } } } } } if (bReverbSettingsFound) { AudioDevice->SetReverbSettings(AudioVolume, ReverbSettings); } // Update level streaming. GetWorld()->UpdateLevelStreaming(); // Draw the player views. if (!bDisableWorldRendering && !bUIDisableWorldRendering && PlayerViewMap.Num() > 0) { GetRendererModule().BeginRenderingViewFamily(SceneCanvas, &ViewFamily); } // Clear areas of the rendertarget (backbuffer) that aren't drawn over by the views. { // Find largest rectangle bounded by all rendered views. uint32 MinX = InViewport->GetSizeXY().X, MinY = InViewport->GetSizeXY().Y, MaxX = 0, MaxY = 0; uint32 TotalArea = 0; for (int32 ViewIndex = 0; ViewIndex < ViewFamily.Views.Num(); ++ViewIndex) { const FSceneView* View = ViewFamily.Views[ViewIndex]; FIntRect UpscaledViewRect = View->UnscaledViewRect; MinX = FMath::Min<uint32>(UpscaledViewRect.Min.X, MinX); MinY = FMath::Min<uint32>(UpscaledViewRect.Min.Y, MinY); MaxX = FMath::Max<uint32>(UpscaledViewRect.Max.X, MaxX); MaxY = FMath::Max<uint32>(UpscaledViewRect.Max.Y, MaxY); TotalArea += FMath::TruncToInt(UpscaledViewRect.Width()) * FMath::TruncToInt(UpscaledViewRect.Height()); } // To draw black borders around the rendered image (prevents artifacts from post processing passes that read outside of the image e.g. PostProcessAA) { int32 BlackBorders = 0; // FMath::Clamp(CVarSetBlackBordersEnabled.GetValueOnGameThread(), 0, 10); if (ViewFamily.Views.Num() == 1 && BlackBorders) { MinX += BlackBorders; MinY += BlackBorders; MaxX -= BlackBorders; MaxY -= BlackBorders; TotalArea = (MaxX - MinX) * (MaxY - MinY); } } // If the views don't cover the entire bounding rectangle, clear the entire buffer. if (ViewFamily.Views.Num() == 0 || TotalArea != (MaxX - MinX)*(MaxY - MinY) || bDisableWorldRendering) { SceneCanvas->DrawTile(0, 0, InViewport->GetSizeXY().X, InViewport->GetSizeXY().Y, 0.0f, 0.0f, 1.0f, 1.f, FLinearColor::Black, NULL, false); } else { // clear left if (MinX > 0) { SceneCanvas->DrawTile(0, 0, MinX, InViewport->GetSizeXY().Y, 0.0f, 0.0f, 1.0f, 1.f, FLinearColor::Black, NULL, false); } // clear right if (MaxX < (uint32)InViewport->GetSizeXY().X) { SceneCanvas->DrawTile(MaxX, 0, InViewport->GetSizeXY().X, InViewport->GetSizeXY().Y, 0.0f, 0.0f, 1.0f, 1.f, FLinearColor::Black, NULL, false); } // clear top if (MinY > 0) { SceneCanvas->DrawTile(MinX, 0, MaxX, MinY, 0.0f, 0.0f, 1.0f, 1.f, FLinearColor::Black, NULL, false); } // clear bottom if (MaxY < (uint32)InViewport->GetSizeXY().Y) { SceneCanvas->DrawTile(MinX, MaxY, MaxX, InViewport->GetSizeXY().Y, 0.0f, 0.0f, 1.0f, 1.f, FLinearColor::Black, NULL, false); } } } // Remove temporary debug lines. if (GetWorld()->LineBatcher != NULL) { GetWorld()->LineBatcher->Flush(); } if (GetWorld()->ForegroundLineBatcher != NULL) { GetWorld()->ForegroundLineBatcher->Flush(); } // Draw FX debug information. if (GetWorld()->FXSystem) { GetWorld()->FXSystem->DrawDebug(SceneCanvas); } // Render the UI. { //SCOPE_CYCLE_COUNTER(STAT_UIDrawingTime); // render HUD bool bDisplayedSubtitles = false; for (FConstPlayerControllerIterator Iterator = GetWorld()->GetPlayerControllerIterator(); Iterator; ++Iterator) { APlayerController* PlayerController = *Iterator; if (PlayerController) { ULocalPlayer* LocalPlayer = Cast<ULocalPlayer>(PlayerController->Player); if (LocalPlayer) { FSceneView* View = PlayerViewMap.FindRef(LocalPlayer); if (View != NULL) { // rendering to directly to viewport target FVector CanvasOrigin(FMath::TruncToFloat(View->UnscaledViewRect.Min.X), FMath::TruncToInt(View->UnscaledViewRect.Min.Y), 0.f); CanvasObject->Init(View->UnscaledViewRect.Width(), View->UnscaledViewRect.Height(), View); // Set the canvas transform for the player's view rectangle. SceneCanvas->PushAbsoluteTransform(FTranslationMatrix(CanvasOrigin)); CanvasObject->ApplySafeZoneTransform(); // Render the player's HUD. if (PlayerController->MyHUD) { //SCOPE_CYCLE_COUNTER(STAT_HudTime); DebugCanvasObject->SceneView = View; PlayerController->MyHUD->SetCanvas(CanvasObject, DebugCanvasObject); if (GEngine->IsStereoscopic3D(InViewport)) { check(GEngine->StereoRenderingDevice.IsValid()); GEngine->StereoRenderingDevice->PushViewportCanvas(eSSP_LEFT_EYE, SceneCanvas, CanvasObject, Viewport); PlayerController->MyHUD->PostRender(); SceneCanvas->PopTransform(); GEngine->StereoRenderingDevice->PushViewportCanvas(eSSP_RIGHT_EYE, SceneCanvas, CanvasObject, Viewport); PlayerController->MyHUD->PostRender(); SceneCanvas->PopTransform(); // Reset the canvas for rendering to the full viewport. CanvasObject->Reset(); CanvasObject->SizeX = View->UnscaledViewRect.Width(); CanvasObject->SizeY = View->UnscaledViewRect.Height(); CanvasObject->SetView(NULL); CanvasObject->Update(); } else { PlayerController->MyHUD->PostRender(); } // Put these pointers back as if a blueprint breakpoint hits during HUD PostRender they can // have been changed CanvasObject->Canvas = SceneCanvas; DebugCanvasObject->Canvas = DebugCanvas; // A side effect of PostRender is that the playercontroller could be destroyed if (!PlayerController->IsPendingKill()) { PlayerController->MyHUD->SetCanvas(NULL, NULL); } } if (DebugCanvas != NULL) { DebugCanvas->PushAbsoluteTransform(FTranslationMatrix(CanvasOrigin)); UDebugDrawService::Draw(ViewFamily.EngineShowFlags, InViewport, View, DebugCanvas); DebugCanvas->PopTransform(); } CanvasObject->PopSafeZoneTransform(); SceneCanvas->PopTransform(); // draw subtitles if (!bDisplayedSubtitles) { FVector2D MinPos(0.f, 0.f); FVector2D MaxPos(1.f, 1.f); GetSubtitleRegion(MinPos, MaxPos); uint32 SizeX = SceneCanvas->GetRenderTarget()->GetSizeXY().X; uint32 SizeY = SceneCanvas->GetRenderTarget()->GetSizeXY().Y; FIntRect SubtitleRegion(FMath::TruncToInt(SizeX * MinPos.X), FMath::TruncToInt(SizeY * MinPos.Y), FMath::TruncToInt(SizeX * MaxPos.X), FMath::TruncToInt(SizeY * MaxPos.Y)); // We need a world to do this FSubtitleManager::GetSubtitleManager()->DisplaySubtitles(SceneCanvas, SubtitleRegion, GetWorld()->GetAudioTimeSeconds()); } } } } } //ensure canvas has been flushed before rendering UI SceneCanvas->Flush_GameThread(); if (DebugCanvas != NULL) { DebugCanvas->Flush_GameThread(); } // Allow the viewport to render additional stuff PostRender(DebugCanvasObject); // Render the console. if (ViewportConsole) { if (GEngine->IsStereoscopic3D(InViewport)) { GEngine->StereoRenderingDevice->PushViewportCanvas(eSSP_LEFT_EYE, DebugCanvas, DebugCanvasObject, Viewport); ViewportConsole->PostRender_Console(DebugCanvasObject); #if !UE_BUILD_SHIPPING if (DebugCanvas != NULL && GEngine->HMDDevice.IsValid()) { GEngine->HMDDevice->DrawDebug(DebugCanvasObject, eSSP_LEFT_EYE); } #endif DebugCanvas->PopTransform(); GEngine->StereoRenderingDevice->PushViewportCanvas(eSSP_RIGHT_EYE, DebugCanvas, DebugCanvasObject, Viewport); ViewportConsole->PostRender_Console(DebugCanvasObject); #if !UE_BUILD_SHIPPING if (DebugCanvas != NULL && GEngine->HMDDevice.IsValid()) { GEngine->HMDDevice->DrawDebug(DebugCanvasObject, eSSP_RIGHT_EYE); } #endif DebugCanvas->PopTransform(); // Reset the canvas for rendering to the full viewport. DebugCanvasObject->Reset(); DebugCanvasObject->SizeX = Viewport->GetSizeXY().X; DebugCanvasObject->SizeY = Viewport->GetSizeXY().Y; DebugCanvasObject->SetView(NULL); DebugCanvasObject->Update(); } else { ViewportConsole->PostRender_Console(DebugCanvasObject); } } } // Grab the player camera location and orientation so we can pass that along to the stats drawing code. FVector PlayerCameraLocation = FVector::ZeroVector; FRotator PlayerCameraRotation = FRotator::ZeroRotator; { for (FConstPlayerControllerIterator Iterator = GetWorld()->GetPlayerControllerIterator(); Iterator; ++Iterator) { (*Iterator)->GetPlayerViewPoint(PlayerCameraLocation, PlayerCameraRotation); } } if (GEngine->IsStereoscopic3D(InViewport)) { GEngine->StereoRenderingDevice->PushViewportCanvas(eSSP_LEFT_EYE, DebugCanvas, DebugCanvasObject, InViewport); DrawStatsHUD(GetWorld(), InViewport, DebugCanvas, DebugCanvasObject, DebugProperties, PlayerCameraLocation, PlayerCameraRotation); DebugCanvas->PopTransform(); GEngine->StereoRenderingDevice->PushViewportCanvas(eSSP_RIGHT_EYE, DebugCanvas, DebugCanvasObject, InViewport); DrawStatsHUD(GetWorld(), InViewport, DebugCanvas, DebugCanvasObject, DebugProperties, PlayerCameraLocation, PlayerCameraRotation); DebugCanvas->PopTransform(); // Reset the canvas for rendering to the full viewport. DebugCanvasObject->Reset(); DebugCanvasObject->SizeX = Viewport->GetSizeXY().X; DebugCanvasObject->SizeY = Viewport->GetSizeXY().Y; DebugCanvasObject->SetView(NULL); DebugCanvasObject->Update(); #if !UE_BUILD_SHIPPING if (GEngine->HMDDevice.IsValid()) { GEngine->HMDDevice->DrawDebug(DebugCanvasObject, eSSP_FULL); } #endif } else { DrawStatsHUD(GetWorld(), InViewport, DebugCanvas, DebugCanvasObject, DebugProperties, PlayerCameraLocation, PlayerCameraRotation); } }
void TestLightmass() { UE_LOG(LogLightmass, Display, TEXT("\n\n")); UE_LOG(LogLightmass, Display, TEXT("===============================================================================================")); UE_LOG(LogLightmass, Display, TEXT("Running \"unit test\". This will take several seconds, and will end with an assertion.")); UE_LOG(LogLightmass, Display, TEXT("This is on purpose, as it's testing the callstack gathering...")); UE_LOG(LogLightmass, Display, TEXT("===============================================================================================")); UE_LOG(LogLightmass, Display, TEXT("\n\n")); void* Buf = FMemory::Malloc(1024); TArray<int32> TestArray; TestArray.Add(5); TArray<int32> ArrayCopy = TestArray; FVector4 TestVectorA(1, 0, 0, 1); FVector4 TestVectorB(1, 1, 1, 1); FVector4 TestVector = TestVectorA + TestVectorB; FString TestString = FString::Printf(TEXT("Copy has %d, Vector is [%.2f, %.2f, %.2f, %.2f]\n"), ArrayCopy[0], TestVector.X, TestVector.Y, TestVector.Z, TestVector.W); wprintf(*TestString); FMemory::Free(Buf); struct FAlignTester { uint8 A; FMatrix M1; uint8 B; FMatrix M2; uint8 C; FVector4 V; }; FAlignTester AlignTest; checkf(((PTRINT)(&FMatrix::Identity) & 15) == 0, TEXT("Identity matrix unaligned")); checkf(((PTRINT)(&AlignTest.M1) & 15) == 0, TEXT("First matrix unaligned")); checkf(((PTRINT)(&AlignTest.M2) & 15) == 0, TEXT("Second matrix unaligned")); checkf(((PTRINT)(&AlignTest.V) & 15) == 0, TEXT("Vector unaligned")); FGuid Guid(1, 2, 3, 4); UE_LOG(LogLightmass, Display, TEXT("Guid is %s"), *Guid.ToString()); TMap<FString, int32> TestMap; TestMap.Add(FString(TEXT("Five")), 5); TestMap.Add(TEXT("Ten"), 10); UE_LOG(LogLightmass, Display, TEXT("Map[Five] = %d, Map[Ten] = %d"), TestMap.FindRef(TEXT("Five")), TestMap.FindRef(FString(TEXT("Ten")))); FMatrix TestMatrix(FVector(0, 0, 0.1f), FVector(0, 1.0f, 0), FVector(0.9f, 0, 0), FVector(0, 0, 0)); UE_LOG(LogLightmass, Display, TEXT("Mat=\n [%0.2f, %0.2f, %0.2f, %0.2f]\n [%0.2f, %0.2f, %0.2f, %0.2f]\n [%0.2f, %0.2f, %0.2f, %0.2f]\n [%0.2f, %0.2f, %0.2f, %0.2f]"), TestMatrix.M[0][0], TestMatrix.M[0][1], TestMatrix.M[0][2], TestMatrix.M[0][3], TestMatrix.M[1][0], TestMatrix.M[1][1], TestMatrix.M[1][2], TestMatrix.M[1][3], TestMatrix.M[2][0], TestMatrix.M[2][1], TestMatrix.M[2][2], TestMatrix.M[2][3], TestMatrix.M[3][0], TestMatrix.M[3][1], TestMatrix.M[3][2], TestMatrix.M[3][3] ); TestMatrix = TestMatrix.GetTransposed(); UE_LOG(LogLightmass, Display, TEXT("Transposed Mat=\n [%0.2f, %0.2f, %0.2f, %0.2f]\n [%0.2f, %0.2f, %0.2f, %0.2f]\n [%0.2f, %0.2f, %0.2f, %0.2f]\n [%0.2f, %0.2f, %0.2f, %0.2f]"), TestMatrix.M[0][0], TestMatrix.M[0][1], TestMatrix.M[0][2], TestMatrix.M[0][3], TestMatrix.M[1][0], TestMatrix.M[1][1], TestMatrix.M[1][2], TestMatrix.M[1][3], TestMatrix.M[2][0], TestMatrix.M[2][1], TestMatrix.M[2][2], TestMatrix.M[2][3], TestMatrix.M[3][0], TestMatrix.M[3][1], TestMatrix.M[3][2], TestMatrix.M[3][3] ); TestMatrix = TestMatrix.GetTransposed().InverseFast(); UE_LOG(LogLightmass, Display, TEXT("Inverted Mat=\n [%0.2f, %0.2f, %0.2f, %0.2f]\n [%0.2f, %0.2f, %0.2f, %0.2f]\n [%0.2f, %0.2f, %0.2f, %0.2f]\n [%0.2f, %0.2f, %0.2f, %0.2f]"), TestMatrix.M[0][0], TestMatrix.M[0][1], TestMatrix.M[0][2], TestMatrix.M[0][3], TestMatrix.M[1][0], TestMatrix.M[1][1], TestMatrix.M[1][2], TestMatrix.M[1][3], TestMatrix.M[2][0], TestMatrix.M[2][1], TestMatrix.M[2][2], TestMatrix.M[2][3], TestMatrix.M[3][0], TestMatrix.M[3][1], TestMatrix.M[3][2], TestMatrix.M[3][3] ); UE_LOG(LogLightmass, Display, TEXT("sizeof FDirectionalLight = %d, FLight = %d, FDirectionalLightData = %d"), sizeof(FDirectionalLight), sizeof(FLight), sizeof(FDirectionalLightData)); TOctree<float, FTestOctreeSemantics> TestOctree(FVector4(0), 10.0f); TestOctree.AddElement(5); // kDOP test TkDOPTree<FTestCollisionDataProvider, uint16> TestkDOP; FTestCollisionDataProvider TestDataProvider(TestkDOP); FHitResult TestResult; FkDOPBuildCollisionTriangle<uint16> TestTri(0, FVector4(0,0,0,0), FVector4(1,1,1,0), FVector4(2,2,2,0), INDEX_NONE, INDEX_NONE, INDEX_NONE, false, true); TArray<FkDOPBuildCollisionTriangle<uint16> > TestTriangles; TestTriangles.Add(TestTri); TestkDOP.Build(TestTriangles); UE_LOG(LogLightmass, Display, TEXT("\nStarting a thread")); FTestRunnable* TestRunnable = new FTestRunnable; // create a thread with the test runnable, and let it auto-delete itself FRunnableThread* TestThread = FRunnableThread::Create(TestRunnable, TEXT("TestRunnable")); double Start = FPlatformTime::Seconds(); UE_LOG(LogLightmass, Display, TEXT("\nWaiting 4 seconds"), Start); FPlatformProcess::Sleep(4.0f); UE_LOG(LogLightmass, Display, TEXT("%.2f seconds have passed, killing thread"), FPlatformTime::Seconds() - Start); // wait for thread to end double KillStart = FPlatformTime::Seconds(); TestRunnable->Stop(); TestThread->WaitForCompletion(); delete TestThread; delete TestRunnable; UE_LOG(LogLightmass, Display, TEXT("It took %.2f seconds to kill the thread [should be < 1 second]"), FPlatformTime::Seconds() - KillStart); UE_LOG(LogLightmass, Display, TEXT("\n\n")); checkf(5 == 2, TEXT("And boom goes the dynamite\n")); }
FString FHardwareInfo::GetHardwareInfo(const FName SpecIdentifier) { return HardwareDetailsMap.FindRef(SpecIdentifier); }
virtual void Compile(FKismetFunctionContext& Context, UEdGraphNode* Node) override { // Cast the node and get all the input pins UK2Node_Select* SelectNode = Cast<UK2Node_Select>(Node); TArray<UEdGraphPin*> OptionPins; SelectNode->GetOptionPins(OptionPins); UEdGraphPin* IndexPin = SelectNode->GetIndexPin(); // Get the kismet term for the (Condition or Index) that will determine which option to use UEdGraphPin* PinToTry = FEdGraphUtilities::GetNetFromPin(IndexPin); FBPTerminal** ConditionTerm = Context.NetMap.Find(PinToTry); // Get the kismet term for the return value UEdGraphPin* ReturnPin = SelectNode->GetReturnValuePin(); FBPTerminal** ReturnTerm = Context.NetMap.Find(ReturnPin); // Don't proceed if there is no return value or there is no selection if (ConditionTerm != NULL && ReturnTerm != NULL) { FName ConditionalFunctionName = ""; UClass* ConditionalFunctionClass = NULL; SelectNode->GetConditionalFunction(ConditionalFunctionName, &ConditionalFunctionClass); UFunction* ConditionFunction = FindField<UFunction>(ConditionalFunctionClass, ConditionalFunctionName); // Find the local boolean for use in the equality call function below (BoolTerm = result of EqualEqual_IntInt or NotEqual_BoolBool) FBPTerminal* BoolTerm = BoolTermMap.FindRef(SelectNode); // We need to keep a pointer to the previous IfNot statement so it can be linked to the next conditional statement FBlueprintCompiledStatement* PrevIfNotStatement = NULL; // Keep an array of all the unconditional goto statements so we can clean up their jumps after the noop statement is created TArray<FBlueprintCompiledStatement*> GotoStatementList; // Loop through all the options for (int32 OptionIdx = 0; OptionIdx < OptionPins.Num(); OptionIdx++) { // Create a CallFunction statement with the condition function from the Select class FBlueprintCompiledStatement& Statement = Context.AppendStatementForNode(Node); Statement.Type = KCST_CallFunction; Statement.FunctionToCall = ConditionFunction; Statement.FunctionContext = NULL; Statement.bIsParentContext = false; // BoolTerm will be the return value of the condition statement Statement.LHS = BoolTerm; // The condition passed into the Select node Statement.RHS.Add(*ConditionTerm); // 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* LiteralTerm = Context.CreateLocalTerminal(ETerminalSpecification::TS_Literal); LiteralTerm->bIsLiteral = true; LiteralTerm->Type.PinCategory = CompilerContext.GetSchema()->PC_Int; LiteralTerm->Name = FString::Printf(TEXT("%d"), OptionIdx); Statement.RHS.Add(LiteralTerm); // If there is a previous IfNot statement, hook this one to that one for jumping if (PrevIfNotStatement) { Statement.bIsJumpTarget = true; PrevIfNotStatement->TargetLabel = &Statement; } // Create a GotoIfNot statement using the BoolTerm from above as the condition FBlueprintCompiledStatement* IfNotStatement = &Context.AppendStatementForNode(Node); IfNotStatement->Type = KCST_GotoIfNot; IfNotStatement->LHS = BoolTerm; // Create an assignment statement FBlueprintCompiledStatement& AssignStatement = Context.AppendStatementForNode(Node); AssignStatement.Type = KCST_Assignment; AssignStatement.LHS = *ReturnTerm; // Get the kismet term from the option pin UEdGraphPin* OptionPinToTry = FEdGraphUtilities::GetNetFromPin(OptionPins[OptionIdx]); FBPTerminal** OptionTerm = Context.NetMap.Find(OptionPinToTry); if (!OptionTerm) { Context.MessageLog.Error(*LOCTEXT("Error_UnregisterOptionPin", "Unregister option pin @@").ToString(), OptionPins[OptionIdx]); return; } AssignStatement.RHS.Add(*OptionTerm); // Create an unconditional goto to exit the node FBlueprintCompiledStatement& GotoStatement = Context.AppendStatementForNode(Node); GotoStatement.Type = KCST_UnconditionalGoto; GotoStatementList.Add(&GotoStatement); // If this is the last IfNot statement, hook the jump to an error message if (OptionIdx == OptionPins.Num() - 1) { // 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; FName PrintStringFunctionName = ""; UClass* PrintStringFunctionClass = NULL; SelectNode->GetPrintStringFunction(PrintStringFunctionName, &PrintStringFunctionClass); UFunction* PrintFunction = FindField<UFunction>(PrintStringFunctionClass, PrintStringFunctionName); 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; FString SelectionNodeType(TEXT("NONE")); if (IndexPin) { UEnum* EnumObject = Cast<UEnum>(IndexPin->PinType.PinSubCategoryObject.Get()); if (EnumObject != NULL) { SelectionNodeType = EnumObject->GetName(); } else { // Not an enum, so just use the basic type SelectionNodeType = IndexPin->PinType.PinCategory; } } const UEdGraph* OwningGraph = Context.MessageLog.FindSourceObjectTypeChecked<UEdGraph>( SelectNode->GetGraph() ); LiteralStringTerm->Name = FString::Printf(*LOCTEXT("SelectNodeIndexWarning", "Graph %s: Selection Node of type %s failed! Out of bounds indexing of the options. There are only %d options available.").ToString(), (OwningGraph) ? *OwningGraph->GetFullName() : TEXT("NONE"), *SelectionNodeType, OptionPins.Num()); PrintStatement.RHS.Add(LiteralStringTerm); // Hook the IfNot statement's jump target to this statement IfNotStatement->TargetLabel = &PrintStatement; } PrevIfNotStatement = IfNotStatement; } // Create a noop to jump to so the unconditional goto statements can exit the node after successful assignment FBlueprintCompiledStatement& NopStatement = Context.AppendStatementForNode(Node); NopStatement.Type = KCST_Nop; NopStatement.bIsJumpTarget = true; // Loop through the unconditional goto statements and fix their jump targets for (auto It = GotoStatementList.CreateConstIterator(); It; It++) { (*It)->TargetLabel = &NopStatement; } } }
/** Console commands, see embeded usage statement **/ virtual bool Exec( UWorld* Inworld, const TCHAR* Cmd, FOutputDevice& Ar ) { if(FParse::Command(&Cmd,TEXT("LOG"))) { if(FParse::Command(&Cmd,TEXT("LIST"))) // if they didn't use the list command, we will show usage { TArray<FLogCategoryPtrs> Found; FString Cat(FParse::Token(Cmd, 0)); for (TMap<FLogCategoryBase*, FName>::TIterator It(Associations); It; ++It) { FLogCategoryBase* Verb = It.Key(); FString Name = It.Value().ToString(); if (!Cat.Len() || Name.Contains(Cat) ) { Found.Add(FLogCategoryPtrs(Name, ELogVerbosity::Type(Verb->Verbosity), Verb->DebugBreakOnLog)); } } Found.Sort(); for (TArray<FLogCategoryPtrs>::TConstIterator It = Found.CreateConstIterator(); It; ++It) { Ar.Logf(TEXT("%-40s %-12s %s"), *It->Name, FOutputDevice::VerbosityToString(It->Verbosity), It->Postfix ? TEXT(" - DebugBreak") : TEXT("")); } } else { FString Rest(Cmd); Rest = Rest.Trim(); if (Rest.Len()) { if (ProcessLogOnly(Rest, Ar)) { return true; } TMap<FName, uint8> OldValues; for (TMap<FLogCategoryBase*, FName>::TIterator It(Associations); It; ++It) { FLogCategoryBase* Verb = It.Key(); FName Name = It.Value(); OldValues.Add(Name, Verb->Verbosity); } ProcessCmdString(Rest); for (TMap<FLogCategoryBase*, FName>::TIterator It(Associations); It; ++It) { FLogCategoryBase* Verb = It.Key(); FName Name = It.Value(); uint8 OldValue = OldValues.FindRef(Name); if (Verb->Verbosity != OldValue) { Ar.Logf(TEXT("%-40s %-12s %s"), *Name.ToString(), FOutputDevice::VerbosityToString(ELogVerbosity::Type(Verb->Verbosity)), Verb->DebugBreakOnLog ? TEXT(" - DebugBreak") : TEXT("")); } } } else { Ar.Logf( TEXT("------- Log conventions") ); Ar.Logf( TEXT("[cat] = a category for the command to operate on, or 'global' for all categories.") ); Ar.Logf( TEXT("[level] = verbosity level, one of: none, error, warning, display, log, verbose, all, default") ); Ar.Logf( TEXT("At boot time, compiled in default is overridden by ini files setting, which is overridden by command line") ); Ar.Logf( TEXT("------- Log console command usage") ); Ar.Logf( TEXT("Log list - list all log categories") ); Ar.Logf( TEXT("Log list [string] - list all log categories containing a substring") ); Ar.Logf( TEXT("Log reset - reset all log categories to their boot-time default") ); Ar.Logf( TEXT("Log [cat] - toggle the display of the category [cat]") ); Ar.Logf( TEXT("Log [cat] off - disable display of the category [cat]") ); Ar.Logf( TEXT("Log [cat] on - resume display of the category [cat]") ); Ar.Logf( TEXT("Log [cat] only - enables [cat] and disables all other categories")); Ar.Logf( TEXT("Log [cat] [level] - set the verbosity level of the category [cat]") ); Ar.Logf( TEXT("Log [cat] break - toggle the debug break on display of the category [cat]") ); Ar.Logf( TEXT("------- Log command line") ); Ar.Logf( TEXT("-LogCmds=\"[arguments],[arguments]...\" - applies a list of console commands at boot time") ); Ar.Logf( TEXT("-LogCmds=\"foo verbose, bar off\" - turns on the foo category and turns off the bar category") ); Ar.Logf( TEXT("------- Environment variables") ); Ar.Logf( TEXT("Any command line option can be set via the environment variable UE-CmdLineArgs") ); Ar.Logf( TEXT("set UE-CmdLineArgs=\"-LogCmds=foo verbose breakon, bar off\"") ); Ar.Logf( TEXT("------- Config file") ); Ar.Logf( TEXT("[Core.Log]") ); Ar.Logf( TEXT("global=[default verbosity for things not listed later]") ); Ar.Logf( TEXT("[cat]=[level]") ); Ar.Logf( TEXT("foo=verbose break") ); } } return true; } return false; }
void FStatsMemoryDumpCommand::ProcessingUObjectAllocations( const TMap<uint64, FAllocationInfo>& AllocationMap ) { // This code is not optimized. FScopeLogTime SLT( TEXT( "ProcessingUObjectAllocations" ), nullptr, FScopeLogTime::ScopeLog_Seconds ); UE_LOG( LogStats, Warning, TEXT( "Processing UObject allocations" ) ); FDiagnosticTableViewer MemoryReport( *FDiagnosticTableViewer::GetUniqueTemporaryFilePath( TEXT( "MemoryReport-UObject" ) ) ); // Write a row of headings for the table's columns. MemoryReport.AddColumn( TEXT( "Size (bytes)" ) ); MemoryReport.AddColumn( TEXT( "Size (MB)" ) ); MemoryReport.AddColumn( TEXT( "Count" ) ); MemoryReport.AddColumn( TEXT( "UObject class" ) ); MemoryReport.CycleRow(); TMap<FName, FSizeAndCount> UObjectAllocations; // To minimize number of calls to expensive DecodeCallstack. TMap<FName,FName> UObjectCallstackToClassMapping; uint64 NumAllocations = 0; uint64 TotalAllocatedMemory = 0; for( const auto& It : AllocationMap ) { const FAllocationInfo& Alloc = It.Value; FName UObjectClass = UObjectCallstackToClassMapping.FindRef( Alloc.EncodedCallstack ); if( UObjectClass == NAME_None ) { TArray<FString> DecodedCallstack; DecodeCallstack( Alloc.EncodedCallstack, DecodedCallstack ); for( int32 Index = DecodedCallstack.Num() - 1; Index >= 0; --Index ) { NAME_INDEX NameIndex = 0; TTypeFromString<NAME_INDEX>::FromString( NameIndex, *DecodedCallstack[Index] ); const FName LongName = FName( NameIndex, NameIndex, 0 ); const bool bValid = UObjectNames.Contains( LongName ); if( bValid ) { const FString ObjectName = FStatNameAndInfo::GetShortNameFrom( LongName ).GetPlainNameString(); UObjectClass = *ObjectName.Left( ObjectName.Find( TEXT( "//" ) ) );; UObjectCallstackToClassMapping.Add( Alloc.EncodedCallstack, UObjectClass ); break; } } } if( UObjectClass != NAME_None ) { FSizeAndCount& SizeAndCount = UObjectAllocations.FindOrAdd( UObjectClass ); SizeAndCount.Size += Alloc.Size; SizeAndCount.Count += 1; TotalAllocatedMemory += Alloc.Size; NumAllocations++; } } // Dump memory to the log. UObjectAllocations.ValueSort( FSizeAndCountGreater() ); const float MaxPctDisplayed = 0.90f; int32 CurrentIndex = 0; uint64 DisplayedSoFar = 0; UE_LOG( LogStats, Warning, TEXT( "Index, Size (Size MB), Count, UObject class" ) ); for( const auto& It : UObjectAllocations ) { const FSizeAndCount& SizeAndCount = It.Value; const FName& UObjectClass = It.Key; UE_LOG( LogStats, Log, TEXT( "%2i, %llu (%.2f MB), %llu, %s" ), CurrentIndex, SizeAndCount.Size, SizeAndCount.Size / 1024.0f / 1024.0f, SizeAndCount.Count, *UObjectClass.GetPlainNameString() ); // Dump stats MemoryReport.AddColumn( TEXT( "%llu" ), SizeAndCount.Size ); MemoryReport.AddColumn( TEXT( "%.2f MB" ), SizeAndCount.Size / 1024.0f / 1024.0f ); MemoryReport.AddColumn( TEXT( "%llu" ), SizeAndCount.Count ); MemoryReport.AddColumn( *UObjectClass.GetPlainNameString() ); MemoryReport.CycleRow(); CurrentIndex++; DisplayedSoFar += SizeAndCount.Size; const float CurrentPct = (float)DisplayedSoFar / (float)TotalAllocatedMemory; if( CurrentPct > MaxPctDisplayed ) { break; } } UE_LOG( LogStats, Warning, TEXT( "Allocated memory: %llu bytes (%.2f MB)" ), TotalAllocatedMemory, TotalAllocatedMemory / 1024.0f / 1024.0f ); // Add a total row. MemoryReport.CycleRow(); MemoryReport.CycleRow(); MemoryReport.CycleRow(); MemoryReport.AddColumn( TEXT( "%llu" ), TotalAllocatedMemory ); MemoryReport.AddColumn( TEXT( "%.2f MB" ), TotalAllocatedMemory / 1024.0f / 1024.0f ); MemoryReport.AddColumn( TEXT( "%llu" ), NumAllocations ); MemoryReport.AddColumn( TEXT( "TOTAL" ) ); MemoryReport.CycleRow(); }
bool FDataTableEditorUtils::MoveRow(UDataTable* DataTable, FName RowName, ERowMoveDirection Direction, int32 NumRowsToMoveBy) { if (!DataTable) { return false; } // Our maps are ordered which is why we can get away with this // If we ever change our map implementation, we'll need to preserve this order information in a separate array and // make sure that order dependent code (such as exporting and the data table viewer) use that when dealing with rows // This may also require making RowMap private and fixing up all the existing code that references it directly TArray<FName> OrderedRowNames; DataTable->RowMap.GenerateKeyArray(OrderedRowNames); const int32 CurrentRowIndex = OrderedRowNames.IndexOfByKey(RowName); if (CurrentRowIndex == INDEX_NONE) { return false; } // Calculate our new row index, clamped to the available rows int32 NewRowIndex = INDEX_NONE; switch(Direction) { case ERowMoveDirection::Up: NewRowIndex = FMath::Clamp(CurrentRowIndex - NumRowsToMoveBy, 0, OrderedRowNames.Num() - 1); break; case ERowMoveDirection::Down: NewRowIndex = FMath::Clamp(CurrentRowIndex + NumRowsToMoveBy, 0, OrderedRowNames.Num() - 1); break; default: break; } if (NewRowIndex == INDEX_NONE) { return false; } if (CurrentRowIndex == NewRowIndex) { // Nothing to do, but not an error return true; } // Swap the order around as requested OrderedRowNames.RemoveAt(CurrentRowIndex, 1, false); OrderedRowNames.Insert(RowName, NewRowIndex); // Build a name -> index map as the KeySort will hit this a lot TMap<FName, int32> NamesToNewIndex; for (int32 NameIndex = 0; NameIndex < OrderedRowNames.Num(); ++NameIndex) { NamesToNewIndex.Add(OrderedRowNames[NameIndex], NameIndex); } const FScopedTransaction Transaction(LOCTEXT("MoveDataTableRow", "Move Data Table Row")); BroadcastPreChange(DataTable, EDataTableChangeInfo::RowList); DataTable->Modify(); // Re-sort the map keys to match the new order DataTable->RowMap.KeySort([&NamesToNewIndex](const FName& One, const FName& Two) -> bool { const int32 OneIndex = NamesToNewIndex.FindRef(One); const int32 TwoIndex = NamesToNewIndex.FindRef(Two); return OneIndex < TwoIndex; }); BroadcastPostChange(DataTable, EDataTableChangeInfo::RowList); return true; }
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; }
virtual void Compile(FKismetFunctionContext& Context, UEdGraphNode* Node) override { UK2Node_Switch* SwitchNode = CastChecked<UK2Node_Switch>(Node); FEdGraphPinType ExpectedExecPinType; ExpectedExecPinType.PinCategory = UEdGraphSchema_K2::PC_Exec; // Make sure that the input pin is connected and valid for this block UEdGraphPin* ExecTriggeringPin = Context.FindRequiredPinByName(SwitchNode, UEdGraphSchema_K2::PN_Execute, EGPD_Input); if ((ExecTriggeringPin == NULL) || !Context.ValidatePinType(ExecTriggeringPin, ExpectedExecPinType)) { CompilerContext.MessageLog.Error(*FString::Printf(*LOCTEXT("NoValidExecutionPinForSwitch_Error", "@@ must have a valid execution pin @@").ToString()), SwitchNode, ExecTriggeringPin); return; } // Make sure that the selection pin is connected and valid for this block UEdGraphPin* SelectionPin = SwitchNode->GetSelectionPin(); if ((SelectionPin == NULL) || !Context.ValidatePinType(SelectionPin, SwitchNode->GetPinType())) { CompilerContext.MessageLog.Error(*FString::Printf(*LOCTEXT("NoValidSelectionPinForSwitch_Error", "@@ must have a valid execution pin @@").ToString()), SwitchNode, SelectionPin); return; } // Find the boolean intermediate result term, so we can track whether the compare was successful FBPTerminal* BoolTerm = BoolTermMap.FindRef(SwitchNode); // Generate the output impulse from this node UEdGraphPin* SwitchSelectionNet = FEdGraphUtilities::GetNetFromPin(SelectionPin); FBPTerminal* SwitchSelectionTerm = Context.NetMap.FindRef(SwitchSelectionNet); if ((BoolTerm != NULL) && (SwitchSelectionTerm != NULL)) { UEdGraphNode* TargetNode = NULL; UEdGraphPin* FuncPin = SwitchNode->GetFunctionPin(); FBPTerminal* FuncContext = Context.NetMap.FindRef(FuncPin); UEdGraphPin* DefaultPin = SwitchNode->GetDefaultPin(); // Pull out function to use UClass* FuncClass = Cast<UClass>(FuncPin->PinType.PinSubCategoryObject.Get()); UFunction* FunctionPtr = FindField<UFunction>(FuncClass, *FuncPin->PinName); check(FunctionPtr); // Run thru all the output pins except for the default label for (auto PinIt = SwitchNode->Pins.CreateIterator(); PinIt; ++PinIt) { UEdGraphPin* Pin = *PinIt; if ((Pin->Direction == EGPD_Output) && (Pin != DefaultPin)) { // Create a term for the switch case value FBPTerminal* CaseValueTerm = new (Context.Literals) FBPTerminal(); CaseValueTerm->Name = Pin->PinName; CaseValueTerm->Type = SelectionPin->PinType; CaseValueTerm->SourcePin = Pin; CaseValueTerm->bIsLiteral = true; // Call the comparison function associated with this switch node FBlueprintCompiledStatement& Statement = Context.AppendStatementForNode(SwitchNode); Statement.Type = KCST_CallFunction; Statement.FunctionToCall = FunctionPtr; Statement.FunctionContext = FuncContext; Statement.bIsParentContext = false; Statement.LHS = BoolTerm; Statement.RHS.Add(SwitchSelectionTerm); Statement.RHS.Add(CaseValueTerm); // Jump to output if strings are actually equal FBlueprintCompiledStatement& IfFailTest_SucceedAtBeingEqualGoto = Context.AppendStatementForNode(SwitchNode); IfFailTest_SucceedAtBeingEqualGoto.Type = KCST_GotoIfNot; IfFailTest_SucceedAtBeingEqualGoto.LHS = BoolTerm; Context.GotoFixupRequestMap.Add(&IfFailTest_SucceedAtBeingEqualGoto, Pin); } } // Finally output default pin GenerateSimpleThenGoto(Context, *SwitchNode, DefaultPin); } else { CompilerContext.MessageLog.Error(*LOCTEXT("ResolveTermPassed_Error", "Failed to resolve term passed into @@").ToString(), SelectionPin); } }