ue小知识点 world partition 一个actor一个文件

			WorldPartition->RuntimeHash->ForEachStreamingCellsSources(StreamingSources, [this](const UWorldPartitionRuntimeCell* Cell, EStreamingSourceTargetState TargetState)
			{
				switch (TargetState)
				{
				case EStreamingSourceTargetState::Loaded:
					FrameLoadCells.Add(Cell);
					break;
				case EStreamingSourceTargetState::Activated:
					FrameActivateCells.Add(Cell);
					break;
				default:
					check(0);
				}

				return true;
			});
void UWorldPartitionRuntimeSpatialHash::ForEachStreamingCellsSources(const TArray<FWorldPartitionStreamingSource>& Sources, TFunctionRef<bool(const UWorldPartitionRuntimeCell*, EStreamingSourceTargetState)> Func) const
{
    //獲得管理datalayer的subsystem
	const UDataLayerSubsystem* DataLayerSubsystem = GetWorld()->GetSubsystem<UDataLayerSubsystem>();

	FStreamingSourceCells ActivateStreamingSourceCells;
	FStreamingSourceCells LoadStreamingSourceCells;
    //如果沒有source,就要處理always load的 cell
	if (Sources.Num() == 0)
	{
		// Get always loaded cells
		ForEachStreamingGrid([&] (const FSpatialHashStreamingGrid& StreamingGrid)
		{
			if (IsCellRelevantFor(StreamingGrid.bClientOnlyVisible))
			{
				StreamingGrid.GetAlwaysLoadedCells(DataLayerSubsystem, ActivateStreamingSourceCells.GetCells(), LoadStreamingSourceCells.GetCells());
			}
		});
    }
	else
	{
		// Get cells based on streaming sources
		ForEachStreamingGrid([&](const FSpatialHashStreamingGrid& StreamingGrid)
		{
			if (IsCellRelevantFor(StreamingGrid.bClientOnlyVisible))
			{
                //根據sources處理cell
				StreamingGrid.GetCells(Sources, DataLayerSubsystem, ActivateStreamingSourceCells, LoadStreamingSourceCells, GetEffectiveEnableZCulling(bEnableZCulling));
			}
		});
	}

	for (const UWorldPartitionRuntimeCell* Cell : ActivateStreamingSourceCells.GetCells())
	{
		if (!Func(Cell, EStreamingSourceTargetState::Activated))
		{
			return;
		}
	}

	for (const UWorldPartitionRuntimeCell* Cell : LoadStreamingSourceCells.GetCells())
	{
		if (!Func(Cell, EStreamingSourceTargetState::Loaded))
		{
			return;
		}
	}
}

//FSpatialHashStreamingGrid---->FSpatialHashStreamingGridLevel---->FSpatialHashStreamingGridLayerCell------>UWorldPartitionRuntimeSpatialHashCell
//有個疑問cell 也有datalayer屬性,不應該只有actor才有datalayer嗎
void FSpatialHashStreamingGrid::GetAlwaysLoadedCells(const UDataLayerSubsystem* DataLayerSubsystem, TSet<const UWorldPartitionRuntimeCell*>& OutActivateCells, TSet<const UWorldPartitionRuntimeCell*>& OutLoadCells) const
{
    //這個grid上有很多個grid level
	if (GridLevels.Num() > 0)
	{
		const int32 TopLevel = GridLevels.Num() - 1;
		for (const FSpatialHashStreamingGridLayerCell& LayerCell : GridLevels[TopLevel].LayerCells)
		{
			for (const UWorldPartitionRuntimeCell* Cell : LayerCell.GridCells)
			{
                //要麽這個cell沒有datalayer,要麽有,其中有一個datalayer是active狀態
				if (!Cell->HasDataLayers() || (DataLayerSubsystem && DataLayerSubsystem->IsAnyDataLayerInEffectiveRuntimeState(Cell->GetDataLayers(), EDataLayerRuntimeState::Activated)))
				{
					check(Cell->IsAlwaysLoaded() || Cell->HasDataLayers() || Cell->GetContentBundleID().IsValid());
					OutActivateCells.Add(Cell);
				}
				else if(DataLayerSubsystem && DataLayerSubsystem->IsAnyDataLayerInEffectiveRuntimeState(Cell->GetDataLayers(), EDataLayerRuntimeState::Loaded))
				{
					check(Cell->HasDataLayers());
					OutLoadCells.Add(Cell);
				}
			}
		}
	}
}
void FSpatialHashStreamingGrid::GetCells(const TArray<FWorldPartitionStreamingSource>& Sources, const UDataLayerSubsystem* DataLayerSubsystem, UWorldPartitionRuntimeHash::FStreamingSourceCells& OutActivateCells, UWorldPartitionRuntimeHash::FStreamingSourceCells& OutLoadCells, bool bEnableZCulling) const
{
	TRACE_CPUPROFILER_EVENT_SCOPE(FSpatialHashStreamingGrid::GetCells);

	struct FStreamingSourceInfo
	{
		FStreamingSourceInfo(const FWorldPartitionStreamingSource& InSource, const FSphericalSector& InSourceShape)
			: Source(InSource)
			, SourceShape(InSourceShape)
		{}
		const FWorldPartitionStreamingSource& Source;
		const FSphericalSector& SourceShape;
	};

	typedef TMap<FGridCellCoord, TArray<FStreamingSourceInfo>> FIntersectingCells;
	FIntersectingCells AllActivatedCells;

	const float GridLoadingRange = GetLoadingRange();
	const FSquare2DGridHelper& Helper = GetGridHelper();
	for (const FWorldPartitionStreamingSource& Source : Sources)
	{
		Source.ForEachShape(GridLoadingRange, GridName, HLODLayer, /*bProjectIn2D*/ true, [&](const FSphericalSector& Shape)
		{
			FStreamingSourceInfo Info(Source, Shape);

			Helper.ForEachIntersectingCells(Shape, [&](const FGridCellCoord& Coords)
			{
				bool bAddedActivatedCell = false;

#if !UE_BUILD_SHIPPING
				if ((GFilterRuntimeSpatialHashGridLevel == INDEX_NONE) || (GFilterRuntimeSpatialHashGridLevel == Coords.Z))
#endif
				{
					if (const FSpatialHashStreamingGridLayerCell* LayerCell = GetLayerCell(Coords))
					{
						for (const UWorldPartitionRuntimeCell* Cell : LayerCell->GridCells)
						{
							if (!bEnableZCulling || TRange<double>(Cell->GetMinMaxZ().X, Cell->GetMinMaxZ().Y).Overlaps(TRange<double>(Shape.GetCenter().Z - Shape.GetRadius(), Shape.GetCenter().Z + Shape.GetRadius())))
							{
								if (!Cell->HasDataLayers() || (DataLayerSubsystem && DataLayerSubsystem->IsAnyDataLayerInEffectiveRuntimeState(Cell->GetDataLayers(), EDataLayerRuntimeState::Activated)))
								{
									if (Source.TargetState == EStreamingSourceTargetState::Loaded)
									{
										OutLoadCells.AddCell(Cell, Source, Shape);
									}
									else
									{
										check(Source.TargetState == EStreamingSourceTargetState::Activated);
										OutActivateCells.AddCell(Cell, Source, Shape);
										bAddedActivatedCell = !GRuntimeSpatialHashUseAlignedGridLevels && GRuntimeSpatialHashSnapNonAlignedGridLevelsToLowerLevels;
									}
								}
								else if (DataLayerSubsystem && DataLayerSubsystem->IsAnyDataLayerInEffectiveRuntimeState(Cell->GetDataLayers(), EDataLayerRuntimeState::Loaded))
								{
									OutLoadCells.AddCell(Cell, Source, Shape);
								}
							}
						}
					}
				}
				if (bAddedActivatedCell)
				{
					AllActivatedCells.FindOrAdd(Coords).Add(Info);
				}
			});
		});
	}

	GetAlwaysLoadedCells(DataLayerSubsystem, OutActivateCells.GetCells(), OutLoadCells.GetCells());

	if (!GRuntimeSpatialHashUseAlignedGridLevels && GRuntimeSpatialHashSnapNonAlignedGridLevelsToLowerLevels)
	{
		auto FindIntersectingParents = [&Helper, this](const FIntersectingCells& InAllCells, const FIntersectingCells& InTestCells, FIntersectingCells& OutIntersectingCells)
		{
			bool bFound = false;
			const int32 AlwaysLoadedLevel = Helper.Levels.Num() - 1;
			for (const auto& InTestCell : InTestCells)
			{
				const FGridCellCoord& TestCell = InTestCell.Key;
				int32 CurrentLevelIndex = TestCell.Z;
				int32 ParentLevelIndex = CurrentLevelIndex + 1;
				// Only test with Parent Level if it's below the AlwaysLoaded Level
				if (ParentLevelIndex < AlwaysLoadedLevel)
				{
					FBox2D CurrentLevelCellBounds;
					Helper.Levels[CurrentLevelIndex].GetCellBounds(FGridCellCoord2(TestCell.X, TestCell.Y), CurrentLevelCellBounds);
					FBox Box(FVector(CurrentLevelCellBounds.Min, 0), FVector(CurrentLevelCellBounds.Max, 0));

					Helper.ForEachIntersectingCells(Box, [&](const FGridCellCoord& IntersectingCoords)
					{
						check(IntersectingCoords.Z >= ParentLevelIndex);
						if (!InAllCells.Contains(IntersectingCoords))
						{
							if (!OutIntersectingCells.Contains(IntersectingCoords))
							{
								OutIntersectingCells.Add(IntersectingCoords, InTestCell.Value);
								bFound = true;
							}
						}
					}, ParentLevelIndex);
				}
			}
			return bFound;
		};
	
		FIntersectingCells AllParentCells;
		FIntersectingCells TestCells = AllActivatedCells;
		FIntersectingCells IntersectingCells;
		bool bFound = false;
		do
		{
			bFound = FindIntersectingParents(AllActivatedCells, TestCells, IntersectingCells);
			if (bFound)
			{
				AllActivatedCells.Append(IntersectingCells);
				AllParentCells.Append(IntersectingCells);
				TestCells = MoveTemp(IntersectingCells);
				check(IntersectingCells.IsEmpty());
			}
		} while (bFound);

		for (const auto& ParentCell : AllParentCells)
		{
			if (const FSpatialHashStreamingGridLayerCell* LayerCell = GetLayerCell(ParentCell.Key))
			{
				for (const UWorldPartitionRuntimeCell* Cell : LayerCell->GridCells)
				{
					if (!Cell->HasDataLayers() || (DataLayerSubsystem && DataLayerSubsystem->IsAnyDataLayerInEffectiveRuntimeState(Cell->GetDataLayers(), EDataLayerRuntimeState::Activated)))
					{
						for (const auto& Info : ParentCell.Value)
						{
							OutActivateCells.AddCell(Cell, Info.Source, Info.SourceShape);
						}
					}
				}
			}
		}
	}
}

---------------------------------------------------------------------------------------------------

源碼分析 ue5.1

WorldPartition初始化

void ULevel::OnLevelLoaded()
{
#if WITH_EDITOR
	FixupActorFolders();
#endif

	auto IsValidLevelInstanceWorldPartition = [](UWorldPartition* InWorldPartition)
	{
#if WITH_EDITOR
		return InWorldPartition->CanBeUsedByLevelInstance();
#else
		return false;
#endif
	};

	// 1. Cook commandlet does it's own UWorldPartition::Initialize call in FWorldPartitionCookPackageSplitter::GetGenerateList
	// 2. Do not Initialize if World doesn't have a UWorldPartitionSubsystem (Known case is when WorldType == EWorldType::Inactive)
	if (!IsRunningCookCommandlet() && OwningWorld->HasSubsystem<UWorldPartitionSubsystem>())
	{
		if (UWorldPartition* WorldPartition = GetWorldPartition())
		{
			//
			// When do we need to initialize the associated world partition object?
			//
			//	- When the level is the main world persistent level
			//	- When the sublevel is streamed in the editor (mainly for data layers)
			//	- When the sublevel is streamed in game and the main world is not partitioned
			//
			const bool bIsOwningWorldGameWorld = OwningWorld->IsGameWorld();
			const bool bIsOwningWorldPartitioned = OwningWorld->IsPartitionedWorld();
			const bool bIsValidLevelInstance = IsValidLevelInstanceWorldPartition(WorldPartition);
			const bool bIsMainWorldLevel = OwningWorld->PersistentLevel == this;
			const bool bInitializeForEditor = !bIsOwningWorldGameWorld && bIsValidLevelInstance;
			const bool bInitializeForGame = bIsOwningWorldGameWorld && !bIsOwningWorldPartitioned;

			UE_LOG(LogWorldPartition, Log, TEXT("ULevel::OnLevelLoaded(%s)(bIsOwningWorldGameWorld=%d, bIsOwningWorldPartitioned=%d, bIsValidLevelInstance=%d, InitializeForMainWorld=%d, InitializeForEditor=%d, InitializeForGame=%d)"), 
				*GetTypedOuter<UWorld>()->GetName(), bIsOwningWorldGameWorld ? 1 : 0, bIsOwningWorldPartitioned ? 1 : 0, bIsValidLevelInstance ? 1 : 0, bIsMainWorldLevel ? 1 : 0, bInitializeForEditor ? 1 : 0, bInitializeForGame ? 1 : 0);

			if (bIsMainWorldLevel || bInitializeForEditor || bInitializeForGame)
			{
				FTransform Transform = FTransform::Identity;
				if (ULevelStreaming* LevelStreaming = FLevelUtils::FindStreamingLevel(this))
				{
					Transform = LevelStreaming->LevelTransform;
				}

				// It is allowed for a WorldPartition to already be initialized in the case where a partitioned sub-level 
				// was streamed-out and is streamed-in again, without a CleanupLevel (GC).
				if (!WorldPartition->IsInitialized())
				{
                    //走到這裏
					WorldPartition->Initialize(OwningWorld, Transform);
				}
				else
				{
					check(OwningWorld->IsGameWorld());
					check(WorldPartition->GetInstanceTransform().Equals(Transform));
				}
			}
		}
	}
}




UWorldPartition* ULevel::GetWorldPartition() const
{
	return bIsPartitioned ? GetWorldSettings()->GetWorldPartition() : nullptr;
}

 在WorldPartition->Initialize的最後一行調用

FWorldPartitionEvents::BroadcastWorldPartitionInitialized(World, this);

 調用之後UWorldPartitionSubsystem中RegisteredWorldPartitions就有值了

第一次更新

 

void UEngine::BlockTillLevelStreamingCompleted(UWorld* InWorld)
{
	QUICK_SCOPE_CYCLE_COUNTER(STAT_UEngine_BlockTillLevelStreamingCompleted);

	check(InWorld);
	InWorld->BlockTillLevelStreamingCompleted();
}

void UWorld::InternalUpdateStreamingState()
{
	// Update streaming levels state using streaming volumes.
	// Issues level streaming load/unload requests based on local players being inside/outside level streaming volumes.
	ProcessLevelStreamingVolumes();

	// Update WorldComposition required streaming levels
	if (WorldComposition)
	{
		WorldComposition->UpdateStreamingState();
	}

	// Update World Subsystems required streaming levels
	const TArray<UWorldSubsystem*>& WorldSubsystems = SubsystemCollection.GetSubsystemArray<UWorldSubsystem>(UWorldSubsystem::StaticClass());
	for (UWorldSubsystem* WorldSubsystem : WorldSubsystems)
	{
	    WorldSubsystem->UpdateStreamingState();
	}
}

 然後每幀update的堆棧如下

void UWorldPartitionSubsystem::UpdateStreamingState()
{
	for (UWorldPartition* RegisteredWorldPartition : RegisteredWorldPartitions)
	{
		RegisteredWorldPartition->UpdateStreamingState();
	}
}

void UWorldPartition::UpdateStreamingState()
{
	if (GetWorld()->IsGameWorld())
	{
		StreamingPolicy->UpdateStreamingState();
	}
}

 每幀更新的邏輯

void UWorldPartitionStreamingPolicy::UpdateStreamingState()
{
	TRACE_CPUPROFILER_EVENT_SCOPE(UWorldPartitionStreamingPolicy::UpdateStreamingState);

	UWorld* World = GetWorld();
	check(World);
	check(World->IsGameWorld());

	// Dermine if the World's BlockTillLevelStreamingCompleted was triggered by WorldPartitionStreamingPolicy
	if (bCriticalPerformanceRequestedBlockTillOnWorld && IsInBlockTillLevelStreamingCompleted())
	{
		bCriticalPerformanceRequestedBlockTillOnWorld = false;
		CriticalPerformanceBlockTillLevelStreamingCompletedEpoch = World->GetBlockTillLevelStreamingCompletedEpoch();
	}
	
	// Update streaming sources
	UpdateStreamingSources();
	
	TSet<FName> ClientVisibleLevelNames;
	bool bUpdateEpoch = false;

	check(FrameActivateCells.IsEmpty());
	check(FrameLoadCells.IsEmpty());

	ON_SCOPE_EXIT
	{
		// Reset frame StreamingSourceCells (optimization to avoid reallocation at every call to UpdateStreamingState)
		FrameActivateCells.Reset();
		FrameLoadCells.Reset();
	};

	const bool bIsServer = WorldPartition->IsServer();
	const bool bIsServerStreamingEnabled = WorldPartition->IsServerStreamingEnabled();
	const bool bCanDeactivateOrUnloadCells = !bIsServer || WorldPartition->IsServerStreamingOutEnabled();
	const int32 NewServerStreamingEnabledEpoch = bIsServer ? (bIsServerStreamingEnabled ? 1 : 0) : INT_MIN;

	if (!bIsServer || bIsServerStreamingEnabled || AWorldPartitionReplay::IsPlaybackEnabled(World))
	{
		// When world partition can't stream, all cells must be unloaded
		if (WorldPartition->CanStream() && WorldPartition->RuntimeHash)
		{
			UWorldPartitionRuntimeCell::DirtyStreamingSourceCacheEpoch();

			WorldPartition->RuntimeHash->ForEachStreamingCellsSources(StreamingSources, [this](const UWorldPartitionRuntimeCell* Cell, EStreamingSourceTargetState TargetState)
			{
				switch (TargetState)
				{
				case EStreamingSourceTargetState::Loaded:
					FrameLoadCells.Add(Cell);
					break;
				case EStreamingSourceTargetState::Activated:
					FrameActivateCells.Add(Cell);
					break;
				default:
					check(0);
				}

				return true;
			});
		}
	}

	if (bIsServer)
	{
		// Server will activate all non data layer cells at first and then load/activate/unload data layer cells only when the data layer states change
		if (!bIsServerStreamingEnabled && 
			(ServerStreamingEnabledEpoch == NewServerStreamingEnabledEpoch) &&
			(ContentBundleServerEpoch == FContentBundle::GetContentBundleEpoch()) &&
			(DataLayersStatesServerEpoch == AWorldDataLayers::GetDataLayersStateEpoch()))
		{
			// Server as nothing to do early out
			return; 
		}

		bUpdateEpoch = true;

		// Gather Client visible level names
		if (const UNetDriver* NetDriver = World->GetNetDriver())
		{
			for (UNetConnection* Connection : NetDriver->ClientConnections)
			{
				ClientVisibleLevelNames.Add(Connection->GetClientWorldPackageName());
				ClientVisibleLevelNames.Append(Connection->ClientVisibleLevelNames);
				ClientVisibleLevelNames.Append(Connection->GetClientMakingVisibleLevelNames());
			}
		}

		const UDataLayerSubsystem* DataLayerSubsystem = GetWorld()->GetSubsystem<UDataLayerSubsystem>();
		TSet<FName> EffectiveActiveDataLayerNames = DataLayerSubsystem->GetEffectiveActiveDataLayerNames();
		TSet<FName> EffectiveLoadedDataLayerNames = DataLayerSubsystem->GetEffectiveLoadedDataLayerNames();

		auto AddServerFrameCells = [this, &EffectiveLoadedDataLayerNames, &EffectiveActiveDataLayerNames](const UWorldPartitionRuntimeCell* Cell)
		{
			// Non Data Layer Cells + Active Data Layers
			if (!Cell->HasDataLayers() || Cell->HasAnyDataLayer(EffectiveActiveDataLayerNames))
			{
				FrameActivateCells.Add(Cell);
			}

			// Loaded Data Layers Cells only
			if (EffectiveLoadedDataLayerNames.Num())
			{
				if (Cell->HasDataLayers() && Cell->HasAnyDataLayer(EffectiveLoadedDataLayerNames))
				{
					FrameLoadCells.Add(Cell);
				}
			}
		};

		if (!bIsServerStreamingEnabled)
		{
			if (WorldPartition->RuntimeHash)
			{
				WorldPartition->RuntimeHash->ForEachStreamingCells([this, &AddServerFrameCells](const UWorldPartitionRuntimeCell* Cell)
				{
					AddServerFrameCells(Cell);
					return true;
				});
			}
		}
		else if (!bCanDeactivateOrUnloadCells)
		{
			// When server streaming-out is disabled, revisit existing loaded/activated cells and add them in the proper FrameLoadCells/FrameActivateCells
			for (const UWorldPartitionRuntimeCell* Cell : ActivatedCells)
			{
				AddServerFrameCells(Cell);
			}
			for (const UWorldPartitionRuntimeCell* Cell : LoadedCells)
			{
				AddServerFrameCells(Cell);
			}
		}
	}

	auto ShouldWaitForClientVisibility = [bIsServer, &ClientVisibleLevelNames, &bUpdateEpoch](const UWorldPartitionRuntimeCell* Cell)
	{
		check(bIsServer);
		if (ULevel* Level = Cell->GetLevel())
		{
			if (ClientVisibleLevelNames.Contains(Cell->GetLevel()->GetPackage()->GetFName()))
			{
				UE_CLOG(bUpdateEpoch, LogWorldPartition, Verbose, TEXT("Server epoch update delayed by client visibility"));
				bUpdateEpoch = false;
				return true;
			}
		}
		return false;
	};

	auto ShouldSkipDisabledHLODCell = [](const UWorldPartitionRuntimeCell* Cell)
	{
		return Cell->GetIsHLOD() && !UHLODSubsystem::IsHLODEnabled();
	};

	// Process cells to activate
	auto ProcessCellsToActivate = [this, &ShouldSkipDisabledHLODCell](TSet<const UWorldPartitionRuntimeCell*>& Cells)
	{
		for (TSet<const UWorldPartitionRuntimeCell*>::TIterator It = Cells.CreateIterator(); It; ++It)
		{
			const UWorldPartitionRuntimeCell* Cell = *It;
			if (ShouldSkipCellForPerformance(Cell) || ShouldSkipDisabledHLODCell(Cell))
			{
				It.RemoveCurrent();
			}
			else
			{
				Cell->MergeStreamingSourceInfo();
			}
		}
	};

	// Process cells to load
	auto ProcessCellsToLoad = [this, bIsServer, &ShouldWaitForClientVisibility, &ShouldSkipDisabledHLODCell](TSet<const UWorldPartitionRuntimeCell*>& Cells)
	{
		for (TSet<const UWorldPartitionRuntimeCell*>::TIterator It = Cells.CreateIterator(); It; ++It)
		{
			const UWorldPartitionRuntimeCell* Cell = *It;
			// Only deactivated server cells need to call ShouldWaitForClientVisibility (those part of ActivatedCells)
			const bool bIsServerCellToDeactivate = bIsServer && ActivatedCells.Contains(Cell);
			const bool bShoudSkipServerCell = bIsServerCellToDeactivate && ShouldWaitForClientVisibility(Cell);

			if (bShoudSkipServerCell || ShouldSkipCellForPerformance(Cell) || ShouldSkipDisabledHLODCell(Cell))
			{
				It.RemoveCurrent();
			}
			else
			{
				Cell->MergeStreamingSourceInfo();
			}
		}
	};

	// Process cells to unload
	auto ProcessCellsToUnload = [this, bIsServer, &ShouldWaitForClientVisibility](TSet<const UWorldPartitionRuntimeCell*>& Cells)
	{
		// Only loop for server as ShouldWaitForClientVisibility only concerns server
		if (bIsServer)
		{
			for (TSet<const UWorldPartitionRuntimeCell*>::TIterator It = Cells.CreateIterator(); It; ++It)
			{
				const UWorldPartitionRuntimeCell* Cell = *It;
				if (ShouldWaitForClientVisibility(Cell))
				{
					It.RemoveCurrent();
				}
			}
		}
	};

	// Activation superseeds Loading
	FrameLoadCells = FrameLoadCells.Difference(FrameActivateCells);

	// Sort cells by importance
	auto SortStreamingCells = [this](const TArray<FWorldPartitionStreamingSource>& Sources, const TSet<const UWorldPartitionRuntimeCell*>& InCells, TArray<const UWorldPartitionRuntimeCell*>& OutCells)
	{
		OutCells.Empty(InCells.Num());
		SortStreamingCellsByImportance(InCells, OutCells);
	};

	// Determine cells to activate (sorted by importance)
	TArray<const UWorldPartitionRuntimeCell*> ToActivateCells;
	{
		TSet<const UWorldPartitionRuntimeCell*> ToActivateCellsUnsorted = FrameActivateCells.Difference(ActivatedCells);
		ProcessCellsToActivate(ToActivateCellsUnsorted);
		SortStreamingCells(StreamingSources, ToActivateCellsUnsorted, ToActivateCells);
	}

	// Determine cells to load (sorted by importance)
	TArray<const UWorldPartitionRuntimeCell*> ToLoadCells;
	{
		TSet<const UWorldPartitionRuntimeCell*> ToLoadCellsUnsorted = FrameLoadCells.Difference(LoadedCells);
		ProcessCellsToLoad(ToLoadCellsUnsorted);
		SortStreamingCells(StreamingSources, ToLoadCellsUnsorted, ToLoadCells);
	}

	// Determine cells to unload
	TArray<const UWorldPartitionRuntimeCell*> ToUnloadCells;
	{
		TSet<const UWorldPartitionRuntimeCell*> ToUnloadCellsUnsorted = ActivatedCells.Union(LoadedCells).Difference(FrameActivateCells.Union(FrameLoadCells));
		ProcessCellsToUnload(ToUnloadCellsUnsorted);
		ToUnloadCells = ToUnloadCellsUnsorted.Array();
	}

	if(World->bMatchStarted)
	{
		WORLDPARTITION_LOG_UPDATESTREAMINGSTATE(Verbose);
	}
	else
	{
		WORLDPARTITION_LOG_UPDATESTREAMINGSTATE(Log);
	}

#if !UE_BUILD_SHIPPING
	UpdateDebugCellsStreamingPriority(FrameActivateCells, FrameLoadCells);
#endif

	if (ToUnloadCells.Num() > 0)
	{
		SetCellsStateToUnloaded(ToUnloadCells);
	}

	// Do Activation State first as it is higher prio than Load State (if we have a limited number of loading cells per frame)
	if (ToActivateCells.Num() > 0)
	{
		SetCellsStateToActivated(ToActivateCells);
	}

	if (ToLoadCells.Num() > 0)
	{
		SetCellsStateToLoaded(ToLoadCells);
	}

	// Sort cells and update streaming priority 
	{
		TRACE_CPUPROFILER_EVENT_SCOPE(UWorldPartitionStreamingPolicy::SortCellsAndUpdateStreamingPriority);
		TSet<const UWorldPartitionRuntimeCell*> AddToWorldCells;
		for (const UWorldPartitionRuntimeCell* ActivatedCell : ActivatedCells)
		{
			if (!ActivatedCell->IsAddedToWorld() && !ActivatedCell->IsAlwaysLoaded())
			{
				AddToWorldCells.Add(ActivatedCell);
			}
		}

		SortStreamingCells(StreamingSources, AddToWorldCells, SortedAddToWorldCells);

		// Update level streaming priority so that UWorld::UpdateLevelStreaming will naturally process the levels in the correct order
		const int32 MaxPrio = SortedAddToWorldCells.Num();
		int32 Prio = MaxPrio;
		const ULevel* CurrentPendingVisibility = GetWorld()->GetCurrentLevelPendingVisibility();
		for (const UWorldPartitionRuntimeCell* Cell : SortedAddToWorldCells)
		{
			// Current pending visibility level is the most important
			const bool bIsCellPendingVisibility = CurrentPendingVisibility && (Cell->GetLevel() == CurrentPendingVisibility);
			const int32 SortedPriority = bIsCellPendingVisibility ? MaxPrio + 1 : Prio--;
			Cell->SetStreamingPriority(SortedPriority);
		}
	}

	// Evaluate streaming performance based on cells that should be activated
	UpdateStreamingPerformance(FrameActivateCells);
	
	// Update Epoch if we aren't waiting for clients anymore
	if (bUpdateEpoch)
	{
		DataLayersStatesServerEpoch = AWorldDataLayers::GetDataLayersStateEpoch();
		ContentBundleServerEpoch = FContentBundle::GetContentBundleEpoch();
		ServerStreamingEnabledEpoch = NewServerStreamingEnabledEpoch;
		UE_LOG(LogWorldPartition, Verbose, TEXT("Server epoch updated"));
	}
}

 隨著StreamingSource位置的變化,要每幀都對所有的cell相對於source的遠近排序。增加命中率。

void UWorldPartitionRuntimeSpatialHashCell::MergeStreamingSourceInfo() const
{
	Super::MergeStreamingSourceInfo();

	int32 Count = CachedSourceModulatedDistances.Num();
	check(Count == CachedSourcePriorityWeights.Num());

	if (Count)
	{
		double TotalSourcePriorityWeight = 0.f;
		for (int32 i = 0; i < Count; ++i)
		{
			TotalSourcePriorityWeight += CachedSourcePriorityWeights[i];
		}
		int32 HighestPrioMinDistIndex = 0;
		double WeightedModulatedDistance = 0.f;
		for (int32 i = 0; i < Count; ++i)
		{
			WeightedModulatedDistance += CachedSourceModulatedDistances[i] * CachedSourcePriorityWeights[i] / TotalSourcePriorityWeight;
			
			// Find highest priority source with the minimum modulated distance
			if ((i != 0) &&
				(CachedSourceModulatedDistances[i] < CachedSourceModulatedDistances[HighestPrioMinDistIndex]) &&
				(CachedSourcePriorityWeights[i] >= CachedSourcePriorityWeights[HighestPrioMinDistIndex]))
			{
				HighestPrioMinDistIndex = i;
			}
		}
		// Sorting distance is the minimum between these:
		// - the highest priority source with the minimum modulated distance 
		// - the weighted modulated distance
		CachedSourceSortingDistance = FMath::Min(CachedSourceModulatedDistances[HighestPrioMinDistIndex], WeightedModulatedDistance);
	}
}

-----------------------------------------------------------------------------------------

darta layer 分editor和 runtime 兩種

editor的datalayer方便場景美術組織和管理場景中的actor 比如可以詳細分類植被 岩石 燈光等

runtime的datalayer一般都服務于gameplay邏輯

--------------------------------------------------------------------------------------

一个actor在 __ExternalActors__中的文件名字 是怎么来的?

FString ULevel::GetActorPackageName(UPackage* InLevelPackage, const FString& InActorPath)
{
	// Convert the actor path to lowercase to make sure we get the same hash for case insensitive file systems
	FString ActorPath = InActorPath.ToLower();

	FArchiveMD5 ArMD5;
	ArMD5 << ActorPath;

	FMD5Hash MD5Hash;
	ArMD5.GetHash(MD5Hash);

	FGuid PackageGuid;
	check(MD5Hash.GetSize() == sizeof(FGuid));
	FMemory::Memcpy(&PackageGuid, MD5Hash.GetBytes(), sizeof(FGuid));
	check(PackageGuid.IsValid());

	FString GuidBase36 = PackageGuid.ToString(EGuidFormats::Base36Encoded);
	check(GuidBase36.Len());

	FString BaseDir = GetExternalActorsPath(InLevelPackage);

	TStringBuilderWithBuffer<TCHAR, NAME_SIZE> ActorPackageName;
	ActorPackageName.Append(BaseDir);
	ActorPackageName.Append(TEXT("/"));
	ActorPackageName.Append(*GuidBase36, 2);
	ActorPackageName.Append(TEXT("/"));
	ActorPackageName.Append(*GuidBase36 + 2, 2);
	ActorPackageName.Append(TEXT("/"));
	ActorPackageName.Append(*GuidBase36 + 4);
	return ActorPackageName.ToString();
}

----------------------------------------------------------------------------------

通过世界大纲 选择项

	const TArray<class FLevelEditorViewportClient*>& LevelViewportClient = GEditor->GetLevelViewportClients();

	for (auto& item : LevelViewportClient)
	{
		ILevelEditor* LevelEditor = item->ParentLevelEditor.Pin().Get();
		if (LevelEditor)
		{
			TSharedPtr<ISceneOutliner> SceneOutliner = LevelEditor->GetSceneOutliner();
			TArray<FSceneOutlinerTreeItemPtr> SelectItems;
			SceneOutliner->GetTree().GetSelectedItems(SelectItems);
			UE_LOG(LogTemp, Log, TEXT("LLLLLLYYYYYYYXXXXXXX GetSelectedActorCount:  %d"), SelectItems.Num());

			for (auto& SItem:SelectItems)
			{
				ISceneOutlinerTreeItem* ISOutLinnerTreeItem = SItem.Get();
				if (ISOutLinnerTreeItem->IsA<FActorDescTreeItem>())
				{

void UEditorToolBPLibrary::GotPartitionActorName(const TArray<AActor*>& ActorsArray, TMap<FString, FString>& NamesMap)
{
	for (auto MyActor : ActorsArray)
	{
		if(MyActor)
		{
			UWorld* MyWorld = MyActor->GetWorld();
			if (MyWorld)
			{
				UWorldPartition* MyWorldPartition = MyWorld->GetWorldPartition();
				if (MyWorldPartition)
				{
					UE_LOG(LogTemp, Log, TEXT("ddddddddddddd:  %d"), MyWorldPartition->GetActorDescCount());

					FWorldPartitionActorDesc* dec = MyWorldPartition->GetActorDesc(MyActor->GetActorGuid());
					if (dec)
					{
						FString DisplayName = dec->GetActorLabel().ToString();
						if (NamesMap.Contains(DisplayName))
						{
							UE_LOG(LogTemp, Log, TEXT("LLLLLLYYYYYYYXXXXXXX repeat name:  %s"), *DisplayName);
						}
						else
						{
							NamesMap.Add(dec->GetActorLabel().ToString(), dec->GetActorPackage().ToString());

						}
					}
				}
			}
		}
	}
}

获得actor的display name 和 asset name 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值