Files
UnrealEngine/Engine/Plugins/Mutable/Source/MutableValidation/Private/MuV/CustomizableObjectInstanceUpdateUtility.cpp
2025-05-18 13:04:45 +08:00

151 lines
5.5 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "CustomizableObjectInstanceUpdateUtility.h"
#include "Commandlets/Commandlet.h"
#include "MuCO/CustomizableObject.h"
#include "MuCO/CustomizableObjectSystem.h"
#include "Engine/TextureStreamingTypes.h"
#include "SceneTypes.h"
#include "ScopedLogSection.h"
#include "ValidationUtils.h"
#include "Components/SkeletalMeshComponent.h"
bool FCustomizableObjectInstanceUpdateUtility::UpdateInstance(UCustomizableObjectInstance& InInstance)
{
LLM_SCOPE_BYNAME(TEXT("FCustomizableObjectInstanceUpdateUtility/UpdateInstance"));
const FScopedLogSection UpdateSection (EMutableLogSection::Update);
check (ComponentsBeingUpdated.IsEmpty());
// Cache the instance being updated for reference once in the update end callback
Instance = TStrongObjectPtr(&InInstance);
// Schedule the update of the COI
{
UE_LOG(LogMutable, Display, TEXT("Invoking update for %s instance."), *Instance->GetName());
// Instance update delegate
FInstanceUpdateNativeDelegate InstanceUpdateNativeDelegate;
InstanceUpdateNativeDelegate.AddSP(this, &FCustomizableObjectInstanceUpdateUtility::OnInstanceUpdateResult);
bIsInstanceBeingUpdated = true;
Instance->UpdateSkeletalMeshAsyncResult(InstanceUpdateNativeDelegate, true, true);
}
// Debug
// FLowLevelMemTracker& LowLevelMemoryTracker = FLowLevelMemTracker::Get();
// Wait until the update has been completed and the mips streamed
while (bIsInstanceBeingUpdated)
{
LLM_SCOPE_BYNAME(TEXT("FCustomizableObjectInstanceUpdateUtility/UpdateLoop"));
// Tick the engine
CommandletHelpers::TickEngine();
// Debug
// LowLevelMemoryTracker.UpdateStatsPerFrame();
// Stop if exit was requested
if (IsEngineExitRequested())
{
break;
}
// Wait until all MIPs gets streamed
if (!ComponentsBeingUpdated.IsEmpty())
{
bool bFullyStreamed = true;
for (TIndexedContainerIterator<TArray<TStrongObjectPtr<USkeletalMeshComponent>>, TArray<TStrongObjectPtr<USkeletalMeshComponent>>::ElementType, TArray<TStrongObjectPtr<USkeletalMeshComponent>>::SizeType> It = ComponentsBeingUpdated.CreateIterator(); It && bFullyStreamed; ++It)
{
TStrongObjectPtr<USkeletalMeshComponent>& ComponentBeingUpdated = *It;
FStreamingTextureLevelContext LevelContext(EMaterialQualityLevel::Num, ComponentBeingUpdated.Get());
TArray<FStreamingRenderAssetPrimitiveInfo> RenderAssetInfoArray;
ComponentBeingUpdated->GetStreamingRenderAssetInfo(LevelContext, RenderAssetInfoArray);
for (TIndexedContainerIterator<TArray<FStreamingRenderAssetPrimitiveInfo>, TArray<
FStreamingRenderAssetPrimitiveInfo>::ElementType, TArray<
FStreamingRenderAssetPrimitiveInfo>::SizeType> ItAsset =
RenderAssetInfoArray.CreateIterator(); ItAsset && bFullyStreamed; ++ItAsset)
{
bFullyStreamed = ItAsset->RenderAsset->IsFullyStreamedIn();
}
}
if (bFullyStreamed)
{
UE_LOG(LogMutable, Display, TEXT("Instance %s finished streaming all MIPs."), *Instance->GetName());
ComponentsBeingUpdated.Reset();
bIsInstanceBeingUpdated = false; // Exit the while loop
}
}
}
// Return true if the update was successful and false if not
return !bInstanceFailedUpdate;
}
void FCustomizableObjectInstanceUpdateUtility::OnInstanceUpdateResult(const FUpdateContext& Result)
{
LLM_SCOPE_BYNAME(TEXT("FCustomizableObjectInstanceUpdateUtility/OnInstanceUpdated"));
check(Instance);
const FString InstanceName = Instance->GetName();
if (UCustomizableObjectSystem::IsUpdateResultValid(Result.UpdateResult))
{
UE_LOG(LogMutable, Display, TEXT("Instance %s finished update successfully."), *InstanceName);
bInstanceFailedUpdate = false;
// Request load all MIPs
check(ComponentsBeingUpdated.IsEmpty());
const TArray<FName> InstanceComponents = Instance->GetComponentNames();
// Early out if no components are found in the instance and notify the Utility that the instance finished its update
if (InstanceComponents.IsEmpty())
{
bIsInstanceBeingUpdated = false;
return;
}
for (const FName& ComponentName : InstanceComponents)
{
TStrongObjectPtr<USkeletalMeshComponent> SkeletalComponent = TStrongObjectPtr(NewObject<USkeletalMeshComponent>());
SkeletalComponent->SetSkeletalMesh(Instance->GetComponentMeshSkeletalMesh(ComponentName));
ComponentsBeingUpdated.Add(SkeletalComponent);
}
// Request the streaming in of all the components affected by the update
for (TStrongObjectPtr<USkeletalMeshComponent>& ComponentBeingUpdated : ComponentsBeingUpdated)
{
FStreamingTextureLevelContext LevelContext(EMaterialQualityLevel::Num, ComponentBeingUpdated.Get());
TArray<FStreamingRenderAssetPrimitiveInfo> RenderAssetInfoArray;
ComponentBeingUpdated->GetStreamingRenderAssetInfo(LevelContext, RenderAssetInfoArray);
for (const FStreamingRenderAssetPrimitiveInfo& Info : RenderAssetInfoArray)
{
Info.RenderAsset->StreamIn(MAX_int32, true);
}
}
UE_LOG(LogMutable, Display, TEXT("Instance %s requesting the streaming of all MIPs."), *InstanceName);
}
else
{
const FString OutputStatus = UEnum::GetValueAsString(Result.UpdateResult);
UE_LOG(LogMutable, Error, TEXT("Instance %s finished update with anomalous state : %s."), *InstanceName, *OutputStatus);
bInstanceFailedUpdate = true;
// Tell the system the instance finished its update so we can continue the execution without waiting for the mips to stream in
bIsInstanceBeingUpdated = false;
}
}