Files
UnrealEngineUWP/Engine/Source/Runtime/NavigationSystem/Private/NavigationOctree.cpp
danny couture c23f6884fe Async Static Mesh Compilation / Loading
- Add protection against async property accessed during async build and postload.
   - Split PostLoad into 3 steps with the most part now being made async.
   - Make Build/BatchBuild API async when the feature is activated.
   - Add a new StaticMesh compilation manager. (This will be refactory later to reduce duplicated code with texture compiler).
   - Skip RenderState creation for any static mesh component still being built so they only show up when ready.
   - Fixed DistanceField and MeshCard computation that might trigger too soon if fixups are required to RenderData during PostLoad.
   - Dynamic priorisation for building and updating static meshes nearest to the viewport.
   - Implement different PIE modes, current default is to stall only for mesh that might affect nagivation/collision for players/bots.
   - Add a new generic interface for assets being built (still a WIP).
   - Add ability in FrontEnd filters to skip serialization of asset being async compiled and refresh when compilation finishes.
   - Prevent auto-save while textures and static meshes are being built (same as shaders)
   - Logic has been reordered in some Fortnite Building Component to avoid unnecessarily touching static mesh properties.

General Optimizations
   - Use cached thumbnails until shaders/textures/static meshes are ready to improve performance and avoid rendering incomplete thumbnails.

DEBUGGING
 - Can be forcibly enabled/disabled through command-line via -asyncstaticmeshcompilation=[off, on, paused]
 - Can pause staticmesh compilation using Editor.AsyncStaticMeshCompilation = 2 or -asyncstaticmeshcompilation=paused
 - Can manually resume a specified amount of paused compilation using Editor.AsyncStaticMeshCompilationResume [Num]
 - Can forcibly wait on all compilation using Editor.AsyncStaticMeshCompilationFlushAll

BENCHMARKS
 - 2m19s to 17.9s for Loading FortGPUTestbed's LumenTest with an empty local only DDC
 - 2h45m to 5mxxs for Loading Reverb's P_World with an empty local only DDC
 - 17m29 to 10m27s for Loading Apollo_Terrain_Edit with an empty local only DDC

TESTS
 - Loading maps from Reverb, Fortnite, ShooterGame, QAGame, FortGPUTestbed
 - Content browser interactions while still under heavy compilation
 - Importing with Datasmith / Dataprep
 - Working with the new Mesh Modeling Tool
 - Shutting down the editor while loading
 - Most of EngineTest Test Suite

REFERENCES
 - Design https://docs.google.com/document/d/1O4GI1G9AtQN6l0SaGFfXw0DyNhlLunLoEqne5INvxIQ
 - Documentation https://docs.google.com/document/d/1KCdFEMhhcsGwfgOisTVwlOwtsmEd7qbB0V6Tc39Gb10

#rb Francis.Hurteau

[CL 14688146 by danny couture in ue5-main branch]
2020-11-09 07:50:34 -04:00

245 lines
7.7 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "NavigationOctree.h"
#include "AI/Navigation/NavRelevantInterface.h"
#include "NavigationSystem.h"
//----------------------------------------------------------------------//
// FNavigationOctree
//----------------------------------------------------------------------//
FNavigationOctree::FNavigationOctree(const FVector& Origin, float Radius)
: TOctree2<FNavigationOctreeElement, FNavigationOctreeSemantics>(Origin, Radius)
, DefaultGeometryGatheringMode(ENavDataGatheringMode::Instant)
, bGatherGeometry(false)
, NodesMemory(0)
{
INC_DWORD_STAT_BY( STAT_NavigationMemory, sizeof(*this) );
}
FNavigationOctree::~FNavigationOctree()
{
DEC_DWORD_STAT_BY( STAT_NavigationMemory, sizeof(*this) );
DEC_MEMORY_STAT_BY(STAT_Navigation_CollisionTreeMemory, NodesMemory);
ObjectToOctreeId.Empty();
}
void FNavigationOctree::SetDataGatheringMode(ENavDataGatheringModeConfig Mode)
{
check(Mode != ENavDataGatheringModeConfig::Invalid);
DefaultGeometryGatheringMode = ENavDataGatheringMode(Mode);
}
void FNavigationOctree::SetNavigableGeometryStoringMode(ENavGeometryStoringMode NavGeometryMode)
{
bGatherGeometry = (NavGeometryMode == FNavigationOctree::StoreNavGeometry);
}
void FNavigationOctree::DemandLazyDataGathering(FNavigationRelevantData& ElementData)
{
UObject* ElementOb = ElementData.GetOwner();
if (ElementOb == nullptr)
{
return;
}
bool bShrink = false;
const int32 OrgElementMemory = ElementData.GetGeometryAllocatedSize();
if (ElementData.IsPendingLazyGeometryGathering() == true && ElementData.SupportsGatheringGeometrySlices() == false)
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_RecastNavMeshGenerator_LazyGeometryExport);
UActorComponent& ActorComp = *CastChecked<UActorComponent>(ElementOb);
ComponentExportDelegate.ExecuteIfBound(&ActorComp, ElementData);
bShrink = true;
// mark this element as no longer needing geometry gathering
ElementData.bPendingLazyGeometryGathering = false;
}
if (ElementData.IsPendingLazyModifiersGathering())
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_RecastNavMeshGenerator_LazyModifiersExport);
INavRelevantInterface* NavElement = Cast<INavRelevantInterface>(ElementOb);
check(NavElement);
NavElement->GetNavigationData(ElementData);
ElementData.bPendingLazyModifiersGathering = false;
bShrink = true;
}
if (bShrink)
{
// validate exported data
// shrink arrays before counting memory
// it will be reallocated when adding to octree and RemoveNode will have different value returned by GetAllocatedSize()
ElementData.ValidateAndShrink();
}
const int32 ElementMemoryChange = ElementData.GetGeometryAllocatedSize() - OrgElementMemory;
const_cast<FNavigationOctree*>(this)->NodesMemory += ElementMemoryChange;
INC_MEMORY_STAT_BY(STAT_Navigation_CollisionTreeMemory, ElementMemoryChange);
}
void FNavigationOctree::DemandChildLazyDataGathering(FNavigationRelevantData& ElementData, INavRelevantInterface& ChildNavInterface)
{
if (IsLazyGathering(ChildNavInterface))
{
ChildNavInterface.GetNavigationData(ElementData);
ElementData.ValidateAndShrink();
}
}
bool FNavigationOctree::IsLazyGathering(const INavRelevantInterface& ChildNavInterface) const
{
const ENavDataGatheringMode GatheringMode = ChildNavInterface.GetGeometryGatheringMode();
const bool bDoInstantGathering = ((GatheringMode == ENavDataGatheringMode::Default && DefaultGeometryGatheringMode == ENavDataGatheringMode::Instant)
|| GatheringMode == ENavDataGatheringMode::Instant);
return !bDoInstantGathering;
}
void FNavigationOctree::AddNode(UObject* ElementOb, INavRelevantInterface* NavElement, const FBox& Bounds, FNavigationOctreeElement& Element)
{
// we assume NavElement is ElementOb already cast
Element.Bounds = Bounds;
if (NavElement)
{
const bool bDoInstantGathering = !IsLazyGathering(*NavElement);
if (bGatherGeometry)
{
UActorComponent* ActorComp = Cast<UActorComponent>(ElementOb);
if (ActorComp)
{
bool bIsActorCompiling = false;
#if WITH_EDITOR
IInterface_AsyncCompilation* AsyncCompiledActor = Cast<IInterface_AsyncCompilation>(ActorComp);
bIsActorCompiling = AsyncCompiledActor && AsyncCompiledActor->IsCompiling();
#endif
// Skip custom navigation export during async compilation, the node will be invalidated once
// compilation finishes.
if (!bIsActorCompiling)
{
if (bDoInstantGathering)
{
ComponentExportDelegate.ExecuteIfBound(ActorComp, *Element.Data);
}
else
{
Element.Data->bPendingLazyGeometryGathering = true;
Element.Data->bSupportsGatheringGeometrySlices = NavElement && NavElement->SupportsGatheringGeometrySlices();
}
}
}
}
SCOPE_CYCLE_COUNTER(STAT_Navigation_GatheringNavigationModifiersSync);
if (bDoInstantGathering)
{
NavElement->GetNavigationData(*Element.Data);
}
else
{
Element.Data->bPendingLazyModifiersGathering = true;
}
}
// validate exported data
// shrink arrays before counting memory
// it will be reallocated when adding to octree and RemoveNode will have different value returned by GetAllocatedSize()
Element.ValidateAndShrink();
const int32 ElementMemory = Element.GetAllocatedSize();
NodesMemory += ElementMemory;
INC_MEMORY_STAT_BY(STAT_Navigation_CollisionTreeMemory, ElementMemory);
AddElement(Element);
}
void FNavigationOctree::AppendToNode(const FOctreeElementId2& Id, INavRelevantInterface* NavElement, const FBox& Bounds, FNavigationOctreeElement& Element)
{
FNavigationOctreeElement OrgData = GetElementById(Id);
Element = OrgData;
Element.Bounds = Bounds + OrgData.Bounds.GetBox();
if (NavElement)
{
SCOPE_CYCLE_COUNTER(STAT_Navigation_GatheringNavigationModifiersSync);
const bool bDoInstantGathering = !IsLazyGathering(*NavElement);
if (bDoInstantGathering)
{
NavElement->GetNavigationData(*Element.Data);
}
else
{
Element.Data->bPendingChildLazyModifiersGathering = true;
}
}
// validate exported data
// shrink arrays before counting memory
// it will be reallocated when adding to octree and RemoveNode will have different value returned by GetAllocatedSize()
Element.ValidateAndShrink();
const int32 OrgElementMemory = OrgData.GetAllocatedSize();
const int32 NewElementMemory = Element.GetAllocatedSize();
const int32 MemoryDelta = NewElementMemory - OrgElementMemory;
NodesMemory += MemoryDelta;
INC_MEMORY_STAT_BY(STAT_Navigation_CollisionTreeMemory, MemoryDelta);
RemoveElement(Id);
AddElement(Element);
}
void FNavigationOctree::UpdateNode(const FOctreeElementId2& Id, const FBox& NewBounds)
{
FNavigationOctreeElement ElementCopy = GetElementById(Id);
RemoveElement(Id);
ElementCopy.Bounds = NewBounds;
AddElement(ElementCopy);
}
void FNavigationOctree::RemoveNode(const FOctreeElementId2& Id)
{
const FNavigationOctreeElement& Element = GetElementById(Id);
const int32 ElementMemory = Element.GetAllocatedSize();
NodesMemory -= ElementMemory;
DEC_MEMORY_STAT_BY(STAT_Navigation_CollisionTreeMemory, ElementMemory);
RemoveElement(Id);
}
const FNavigationRelevantData* FNavigationOctree::GetDataForID(const FOctreeElementId2& Id) const
{
if (Id.IsValidId() == false)
{
return nullptr;
}
const FNavigationOctreeElement& OctreeElement = GetElementById(Id);
return &*OctreeElement.Data;
}
void FNavigationOctree::SetElementIdImpl(const uint32 OwnerUniqueId, FOctreeElementId2 Id)
{
ObjectToOctreeId.Add(OwnerUniqueId, Id);
}
//----------------------------------------------------------------------//
// FNavigationOctreeSemantics
//----------------------------------------------------------------------//
#if NAVSYS_DEBUG
FORCENOINLINE
#endif // NAVSYS_DEBUG
void FNavigationOctreeSemantics::SetElementId(FNavigationOctreeSemantics::FOctree& OctreeOwner, const FNavigationOctreeElement& Element, FOctreeElementId2 Id)
{
((FNavigationOctree&)OctreeOwner).SetElementIdImpl(Element.OwnerUniqueId, Id);
}