Files
UnrealEngine/Engine/Plugins/Runtime/MassGameplay/Source/MassLOD/Private/MassSimulationLOD.cpp
2025-05-18 13:04:45 +08:00

209 lines
9.9 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "MassSimulationLOD.h"
#include "MassCommonFragments.h"
#include "MassExecutionContext.h"
#include "GameFramework/PlayerController.h"
#include "Engine/World.h"
#include "VisualLogger/VisualLogger.h"
#include "MassLODUtils.h"
//-----------------------------------------------------------------------------
// FMassSimulationLODParameters
//-----------------------------------------------------------------------------
FMassSimulationLODParameters::FMassSimulationLODParameters()
{
LODDistance[EMassLOD::High] = 0.0f;
LODDistance[EMassLOD::Medium] = 5000.0f;
LODDistance[EMassLOD::Low] = 10000.0f;
LODDistance[EMassLOD::Off] = 30000.0f;
LODMaxCount[EMassLOD::High] = 100;
LODMaxCount[EMassLOD::Medium] = 200;
LODMaxCount[EMassLOD::Low] = 300;
LODMaxCount[EMassLOD::Off] = INT_MAX;
}
//-----------------------------------------------------------------------------
// FMassSimulationVariableTickParameters
//-----------------------------------------------------------------------------
FMassSimulationVariableTickParameters::FMassSimulationVariableTickParameters()
{
TickRates[EMassLOD::High] = 0.0f;
TickRates[EMassLOD::Medium] = 0.5f;
TickRates[EMassLOD::Low] = 1.0f;
TickRates[EMassLOD::Off] = 1.5f;
}
//-----------------------------------------------------------------------------
// FMassSimulationLODSharedFragment
//-----------------------------------------------------------------------------
FMassSimulationLODSharedFragment::FMassSimulationLODSharedFragment(const FMassSimulationLODParameters& LODParams)
{
LODCalculator.Initialize(LODParams.LODDistance, LODParams.BufferHysteresisOnDistancePercentage / 100.0f, LODParams.LODMaxCount);
}
//-----------------------------------------------------------------------------
// FMassSimulationLODSharedFragment
//-----------------------------------------------------------------------------
FMassSimulationVariableTickSharedFragment::FMassSimulationVariableTickSharedFragment(const FMassSimulationVariableTickParameters& TickRateParams)
{
LODTickRateController.Initialize(TickRateParams.TickRates, TickRateParams.bSpreadFirstSimulationUpdate);
}
//-----------------------------------------------------------------------------
// UMassSimulationLODProcessor
//-----------------------------------------------------------------------------
namespace UE::MassLOD
{
int32 bDebugSimulationLOD = 0;
FAutoConsoleVariableRef CVarDebugSimulationLODTest(TEXT("mass.debug.SimulationLOD"), bDebugSimulationLOD, TEXT("Debug Simulation LOD"), ECVF_Cheat);
} // UE::MassLOD
UMassSimulationLODProcessor::UMassSimulationLODProcessor()
: EntityQuery(*this)
, EntityQueryCalculateLOD(*this)
, EntityQueryAdjustDistances(*this)
, EntityQueryVariableTick(*this)
, EntityQuerySetLODTag(*this)
{
ExecutionFlags = (int32)EProcessorExecutionFlags::AllNetModes;
ExecutionOrder.ExecuteInGroup = UE::Mass::ProcessorGroupNames::LOD;
ExecutionOrder.ExecuteAfter.Add(UE::Mass::ProcessorGroupNames::LODCollector);
}
void UMassSimulationLODProcessor::ConfigureQueries(const TSharedRef<FMassEntityManager>& EntityManager)
{
EntityQuery.AddRequirement<FTransformFragment>(EMassFragmentAccess::ReadOnly);
EntityQuery.AddRequirement<FMassViewerInfoFragment>(EMassFragmentAccess::ReadOnly);
EntityQuery.AddRequirement<FMassSimulationLODFragment>(EMassFragmentAccess::ReadWrite);
EntityQuery.AddConstSharedRequirement<FMassSimulationLODParameters>();
EntityQuery.AddSharedRequirement<FMassSimulationLODSharedFragment>(EMassFragmentAccess::ReadWrite);
EntityQuery.AddChunkRequirement<FMassSimulationVariableTickChunkFragment>(EMassFragmentAccess::ReadOnly, EMassFragmentPresence::Optional);
EntityQuery.AddSharedRequirement<FMassSimulationVariableTickSharedFragment>(EMassFragmentAccess::ReadOnly, EMassFragmentPresence::Optional);
EntityQueryCalculateLOD = EntityQuery;
EntityQueryCalculateLOD.SetChunkFilter(FMassSimulationVariableTickSharedFragment::ShouldCalculateLODForChunk);
EntityQueryAdjustDistances = EntityQuery;
EntityQueryAdjustDistances.SetChunkFilter([](const FMassExecutionContext& Context)
{
const FMassSimulationLODSharedFragment& LODSharedFragment = Context.GetSharedFragment<FMassSimulationLODSharedFragment>();
return LODSharedFragment.bHasAdjustedDistancesFromCount && FMassSimulationVariableTickSharedFragment::ShouldAdjustLODFromCountForChunk(Context);
});
EntityQueryVariableTick.AddRequirement<FMassSimulationLODFragment>(EMassFragmentAccess::ReadOnly);
EntityQueryVariableTick.AddRequirement<FMassSimulationVariableTickFragment>(EMassFragmentAccess::ReadWrite);
EntityQueryVariableTick.AddConstSharedRequirement<FMassSimulationVariableTickParameters>();
EntityQueryVariableTick.AddChunkRequirement<FMassSimulationVariableTickChunkFragment>(EMassFragmentAccess::ReadWrite);
EntityQueryVariableTick.AddSharedRequirement<FMassSimulationVariableTickSharedFragment>(EMassFragmentAccess::ReadWrite);
// In case where the variableTick isn't enabled, we might need to set LOD tags as if the users still wants them
EntityQuerySetLODTag.AddRequirement<FMassSimulationLODFragment>(EMassFragmentAccess::ReadOnly);
EntityQuerySetLODTag.AddRequirement<FMassSimulationVariableTickFragment>(EMassFragmentAccess::ReadWrite, EMassFragmentPresence::None);
EntityQuerySetLODTag.AddConstSharedRequirement<FMassSimulationLODParameters>();
EntityQuerySetLODTag.SetChunkFilter([](const FMassExecutionContext& Context)
{
const FMassSimulationLODParameters& LODParams = Context.GetConstSharedFragment<FMassSimulationLODParameters>();
return LODParams.bSetLODTags;
});
ProcessorRequirements.AddSubsystemRequirement<UMassLODSubsystem>(EMassFragmentAccess::ReadOnly);
}
void UMassSimulationLODProcessor::Execute(FMassEntityManager& EntityManager, FMassExecutionContext& Context)
{
TRACE_CPUPROFILER_EVENT_SCOPE(SimulationLOD)
{
TRACE_CPUPROFILER_EVENT_SCOPE(PrepareExecution);
const UMassLODSubsystem& LODSubsystem = Context.GetSubsystemChecked<UMassLODSubsystem>();
const TArray<FViewerInfo>& Viewers = LODSubsystem.GetViewers();
EntityManager.ForEachSharedFragment<FMassSimulationLODSharedFragment>([&Viewers](FMassSimulationLODSharedFragment& LODSharedFragment)
{
LODSharedFragment.LODCalculator.PrepareExecution(Viewers);
});
}
{
TRACE_CPUPROFILER_EVENT_SCOPE(CalculateLOD);
EntityQueryCalculateLOD.ForEachEntityChunk(Context, [](FMassExecutionContext& Context)
{
FMassSimulationLODSharedFragment& LODSharedFragment = Context.GetMutableSharedFragment<FMassSimulationLODSharedFragment>();
TConstArrayView<FMassViewerInfoFragment> ViewersInfoList = Context.GetFragmentView<FMassViewerInfoFragment>();
TArrayView<FMassSimulationLODFragment> SimulationLODFragments = Context.GetMutableFragmentView<FMassSimulationLODFragment>();
LODSharedFragment.LODCalculator.CalculateLOD(Context, ViewersInfoList, SimulationLODFragments);
});
}
if (bDoAdjustmentFromCount)
{
TRACE_CPUPROFILER_EVENT_SCOPE(AdjustDistancesAndLODFromCount);
EntityManager.ForEachSharedFragment<FMassSimulationLODSharedFragment>([](FMassSimulationLODSharedFragment& LODSharedFragment)
{
LODSharedFragment.bHasAdjustedDistancesFromCount = LODSharedFragment.LODCalculator.AdjustDistancesFromCount();
});
EntityQueryAdjustDistances.ForEachEntityChunk(Context, [](FMassExecutionContext& Context)
{
FMassSimulationLODSharedFragment& LODSharedFragment = Context.GetMutableSharedFragment<FMassSimulationLODSharedFragment>();
TConstArrayView<FMassViewerInfoFragment> ViewersInfoList = Context.GetFragmentView<FMassViewerInfoFragment>();
TArrayView<FMassSimulationLODFragment> SimulationLODFragments = Context.GetMutableFragmentView<FMassSimulationLODFragment>();
LODSharedFragment.LODCalculator.AdjustLODFromCount(Context, ViewersInfoList, SimulationLODFragments);
});
}
UWorld* World = EntityManager.GetWorld();
{
TRACE_CPUPROFILER_EVENT_SCOPE(VariableTickRates)
check(World);
const double Time = World->GetTimeSeconds();
EntityQueryVariableTick.ForEachEntityChunk(Context, [Time](FMassExecutionContext& Context)
{
FMassSimulationVariableTickSharedFragment& TickRateSharedFragment = Context.GetMutableSharedFragment<FMassSimulationVariableTickSharedFragment>();
TConstArrayView<FMassSimulationLODFragment> SimulationLODFragments = Context.GetFragmentView<FMassSimulationLODFragment>();
TArrayView<FMassSimulationVariableTickFragment> SimulationVariableTickFragments = Context.GetMutableFragmentView<FMassSimulationVariableTickFragment>();
TickRateSharedFragment.LODTickRateController.UpdateTickRateFromLOD(Context, SimulationLODFragments, SimulationVariableTickFragments, Time);
});
}
{
TRACE_CPUPROFILER_EVENT_SCOPE(SetLODTags)
check(World);
EntityQuerySetLODTag.ForEachEntityChunk(Context, [](FMassExecutionContext& Context)
{
TConstArrayView<FMassSimulationLODFragment> SimulationLODFragments = Context.GetFragmentView<FMassSimulationLODFragment>();
for (FMassExecutionContext::FEntityIterator EntityIt = Context.CreateEntityIterator(); EntityIt; ++EntityIt)
{
const FMassSimulationLODFragment& EntityLOD = SimulationLODFragments[EntityIt];
if (EntityLOD.PrevLOD != EntityLOD.LOD)
{
const FMassEntityHandle Entity = Context.GetEntity(EntityIt);
UE::MassLOD::PushSwapTagsCommand(Context.Defer(), Entity, EntityLOD.PrevLOD, EntityLOD.LOD);
}
}
});
}
#if WITH_MASSGAMEPLAY_DEBUG
// Optional debug display
if (UE::MassLOD::bDebugSimulationLOD)
{
TRACE_CPUPROFILER_EVENT_SCOPE(DebugDisplayLOD);
EntityQuery.ForEachEntityChunk(Context, [World](FMassExecutionContext& Context)
{
FMassSimulationLODSharedFragment& LODSharedFragment = Context.GetMutableSharedFragment<FMassSimulationLODSharedFragment>();
TConstArrayView<FTransformFragment> LocationList = Context.GetFragmentView<FTransformFragment>();
TConstArrayView<FMassSimulationLODFragment> SimulationLODList = Context.GetFragmentView<FMassSimulationLODFragment>();
LODSharedFragment.LODCalculator.DebugDisplayLOD(Context, SimulationLODList, LocationList, World);
});
}
#endif // WITH_MASSGAMEPLAY_DEBUG
}