Files
UnrealEngine/Engine/Source/Developer/MassEntityTestSuite/Private/MassMultiThreadTest.cpp
2025-05-18 13:04:45 +08:00

175 lines
5.4 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "MassEntityManager.h"
#include "MassProcessingContext.h"
#include "MassEntityTestTypes.h"
#include "MassEntitySettings.h"
#include "MassExecutor.h"
#include "MassEntityView.h"
#include "MassExecutionContext.h"
#include "MassProcessingContext.h"
#define LOCTEXT_NAMESPACE "MassTest"
UE_DISABLE_OPTIMIZATION_SHIP
//----------------------------------------------------------------------//
// tests
//----------------------------------------------------------------------//
namespace FMassMultiThreadingTest
{
template<typename T>
static FName GetProcessorName()
{
return T::StaticClass()->GetFName();
}
struct FMTTestBase : FEntityTestBase
{
using Super = FExecutionTestBase;
UMassCompositeProcessor* CompositeProcessor = nullptr;
TArray<UMassTestProcessorBase*> Processors;
TArray<FMassProcessorOrderInfo> Result;
FGraphEventRef FinishEvent;
TArray<FMassEntityHandle> Entities;
virtual bool Update() override
{
// cannot do this in SetUp without adding a new virtual function for subtests to override
if (CompositeProcessor == nullptr)
{
CompositeProcessor = NewObject<UMassCompositeProcessor>();
CompositeProcessor->SetGroupName(TEXT("Test"));
CompositeProcessor->SetProcessors(MakeArrayView<UMassProcessor*>((UMassProcessor**)Processors.GetData(), Processors.Num()));
FMassProcessingContext Context(*EntityManager);
FinishEvent = UE::Mass::Executor::TriggerParallelTasks(*CompositeProcessor, MoveTemp(Context), []() {});
}
if (FinishEvent->IsComplete())
{
// signal that we're done with this test
return true;
}
return false;
}
};
struct FMTTrivial : FMTTestBase
{
using Super = FMTTestBase;
const int32 NumToCreate = 200;
int32 NumProcessed = 0;
virtual bool SetUp() override
{
if (!Super::SetUp())
{
return false;
}
EntityManager->BatchCreateEntities(IntsArchetype, NumToCreate, Entities);
Processors.Reset();
{
UMassTestProcessorBase* Proc = Processors.Add_GetRef(NewTestProcessor<UMassTestProcessor_A>(EntityManager));
Proc->EntityQuery.AddRequirement<FTestFragment_Int>(EMassFragmentAccess::ReadOnly);
Proc->ForEachEntityChunkExecutionFunction = [this](FMassExecutionContext& Context)
{
NumProcessed += Context.GetNumEntities();
};
}
return true;
}
virtual void VerifyLatentResults() override
{
AITEST_EQUAL_LATENT("Expected to process all the created entities.", NumToCreate, NumProcessed);
}
};
IMPLEMENT_AI_LATENT_TEST(FMTTrivial, "System.Mass.Multithreading.Trivial");
struct FMTBasic : FMTTestBase
{
using Super = FMTTestBase;
const int32 NumToCreate = 200;
int32 NumProcessed = 0;
virtual bool SetUp() override
{
if (!Super::SetUp())
{
return false;
}
EntityManager->BatchCreateEntities(FloatsIntsArchetype, NumToCreate, Entities);
Processors.Reset();
{
UMassTestProcessorBase* Proc = Processors.Add_GetRef(NewTestProcessor<UMassTestProcessor_C>(EntityManager));
Proc->GetMutableExecutionOrder().ExecuteAfter.Add(GetProcessorName<UMassTestProcessor_B>());
Proc->EntityQuery.AddRequirement<FTestFragment_Float>(EMassFragmentAccess::ReadOnly);
Proc->EntityQuery.AddRequirement<FTestFragment_Int>(EMassFragmentAccess::ReadWrite);
Proc->ForEachEntityChunkExecutionFunction = [this](FMassExecutionContext& Context)
{
const TArrayView<FTestFragment_Int> IntsList = Context.GetMutableFragmentView<FTestFragment_Int>();
const TConstArrayView<FTestFragment_Float> FloatsList = Context.GetFragmentView<FTestFragment_Float>();
for (int32 i = 0; i < Context.GetNumEntities(); ++i)
{
IntsList[i].Value = int(FloatsList[i].Value) + IntsList[i].Value;
}
};
}
{
UMassTestProcessorBase* Proc = Processors.Add_GetRef(NewTestProcessor<UMassTestProcessor_B>(EntityManager));
Proc->GetMutableExecutionOrder().ExecuteAfter.Add(GetProcessorName<UMassTestProcessor_A>());
Proc->EntityQuery.AddRequirement<FTestFragment_Int>(EMassFragmentAccess::ReadOnly);
Proc->EntityQuery.AddRequirement<FTestFragment_Float>(EMassFragmentAccess::ReadWrite);
Proc->ForEachEntityChunkExecutionFunction = [this](FMassExecutionContext& Context)
{
const TConstArrayView<FTestFragment_Int> IntsList = Context.GetFragmentView<FTestFragment_Int>();
const TArrayView<FTestFragment_Float> FloatsList = Context.GetMutableFragmentView<FTestFragment_Float>();
for (int32 i = 0; i < Context.GetNumEntities(); ++i)
{
FloatsList[i].Value = float(IntsList[i].Value * IntsList[i].Value);
}
};
}
{
UMassTestProcessorBase* Proc = Processors.Add_GetRef(NewTestProcessor<UMassTestProcessor_A>(EntityManager));
Proc->EntityQuery.AddRequirement<FTestFragment_Int>(EMassFragmentAccess::ReadWrite);
Proc->ForEachEntityChunkExecutionFunction = [this](FMassExecutionContext& Context)
{
int Index = 0;
const TArrayView<FTestFragment_Int> IntsList = Context.GetMutableFragmentView<FTestFragment_Int>();
for (int32 i = 0; i < Context.GetNumEntities(); ++i)
{
IntsList[i].Value = Index++;
}
};
}
return true;
}
virtual void VerifyLatentResults() override
{
for (int i = 0; i < Entities.Num(); ++i)
{
FMassEntityView View(FloatsIntsArchetype, Entities[i]);
AITEST_EQUAL_LATENT(TEXT("Should have predicted values"), View.GetFragmentData<FTestFragment_Int>().Value, i*i + i);
}
}
};
IMPLEMENT_AI_LATENT_TEST(FMTBasic, "System.Mass.Multithreading.Basic");
} // FMassMultiThreadingTest
UE_ENABLE_OPTIMIZATION_SHIP
#undef LOCTEXT_NAMESPACE