Files
UnrealEngine/Engine/Source/Runtime/MovieScene/Public/EntitySystem/IMovieSceneTaskScheduler.h
2025-05-18 13:04:45 +08:00

228 lines
6.8 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Stats/Stats.h"
#include "Misc/TVariant.h"
#include "EntitySystem/RelativePtr.h"
#include "EntitySystem/MovieSceneEntitySystemTypes.h"
#include "EntitySystem/EntityAllocationIterator.h"
namespace UE::MovieScene
{
/**
* Typedef for a pointer that is relative to another. For most component data this is relative either to the FEntityAllocation itself, or FEntityAllocation::ComponentData
* Using a relative ptr for these allows us to store the same information with half of the memory (or 1/4 if we were able to use uint16 - I've used uint32 for safety here)
*/
using FPreLockedDataPtr = TRelativePtr<void, uint32>;
struct FTaskID
{
int32 Index;
explicit FTaskID()
: Index(INDEX_NONE)
{}
explicit FTaskID(int32 InIndex)
: Index(InIndex)
{}
static FTaskID None()
{
return FTaskID(INDEX_NONE);
}
explicit operator bool() const
{
return Index != INDEX_NONE;
}
};
struct FTaskParams
{
explicit FTaskParams(const TStatId& InStatId)
:
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
DebugName(nullptr),
#endif
StatId(InStatId)
{
bForceGameThread = false;
bSerialTasks = false;
bForcePropagateDownstream = false;
bForceConsumeUpstream = false;
bForcePrePostTask = false;
}
explicit FTaskParams(const TCHAR* InDebugName, const TStatId& InStatId = TStatId())
:
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
DebugName(InDebugName),
#endif
StatId(InStatId)
{
bForceGameThread = false;
bSerialTasks = false;
bForcePropagateDownstream = false;
bForceConsumeUpstream = false;
bForcePrePostTask = false;
}
/**
* Set a custom stat ID for this task
*/
FTaskParams& Stat(const TStatId& InStatId)
{
StatId = InStatId;
return *this;
}
/**
* Force this task to run on the game thread
*/
FTaskParams& ForceGameThread()
{
bForceGameThread = true;
return *this;
}
/**
* Force this task to run Pre/Post callbacks even if there is no meaningful work to be done in the body
*/
FTaskParams& ForcePrePostTask()
{
bForcePrePostTask = true;
return *this;
}
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
const TCHAR* DebugName;
#endif
TStatId StatId;
uint8 bForceGameThread : 1;
uint8 bSerialTasks : 1;
uint8 bForcePrePostTask : 1;
uint8 bForcePropagateDownstream : 1;
uint8 bForceConsumeUpstream : 1;
};
struct ITaskContext
{
virtual ~ITaskContext() {}
};
template<typename TaskType>
struct TAnonTaskWrapper : ITaskContext
{
TaskType Task;
template<typename ...ArgTypes>
explicit TAnonTaskWrapper(ArgTypes&&... InArgs)
: Task{ Forward<ArgTypes>(InArgs)... }
{}
static void Execute(const ITaskContext* Context, FEntityAllocationWriteContext WriteContext)
{
static_cast<const TAnonTaskWrapper<TaskType>*>(Context)->Task.Run(WriteContext);
}
};
template<typename ClassType>
struct TMemberFunctionTaskWrapper : ITaskContext
{
using MemberFunctionPtr = void (ClassType::*)();
ClassType* ClassPtr;
MemberFunctionPtr FunctionPtr;
explicit TMemberFunctionTaskWrapper(ClassType* InClassPtr, MemberFunctionPtr InFunctionPtr)
: ClassPtr(InClassPtr)
, FunctionPtr(InFunctionPtr)
{}
static void Execute(const ITaskContext* Context, FEntityAllocationWriteContext WriteContext)
{
const TMemberFunctionTaskWrapper<ClassType>* This = static_cast<const TMemberFunctionTaskWrapper<ClassType>*>(Context);
(This->ClassPtr->*This->FunctionPtr)();
}
};
using UnboundTaskFunctionPtr = void (*)(const ITaskContext* TaskContext, FEntityAllocationWriteContext WriteContext);
using AllocationFunctionPtr = void (*)(const FEntityAllocation* Allocation, const ITaskContext* TaskContext, FEntityAllocationWriteContext WriteContext);
using AllocationItemFunctionPtr = void (*)(FEntityAllocationIteratorItem Item, const ITaskContext* TaskContext, FEntityAllocationWriteContext WriteContext);
using PreLockedAllocationItemFunctionPtr = void (*)(FEntityAllocationIteratorItem Item, TArrayView<const FPreLockedDataPtr> PreLockedData, const ITaskContext* TaskContext, FEntityAllocationWriteContext WriteContext);
using TaskFunctionPtr = TVariant<UnboundTaskFunctionPtr, AllocationFunctionPtr, AllocationItemFunctionPtr, PreLockedAllocationItemFunctionPtr>;
class IEntitySystemScheduler
{
public:
/**
* Add a new task of the specified type for the currently open node ID
*
* Example usage:
* TaskScheduler->AddTask<FMyTaskType>(FTaskParams(GET_STAT_ID(StatId)));
* TaskScheduler->AddTask<FMyTaskType2>(FTaskParams(GET_STAT_ID(StatId)), ConstructorArg1, ConstructorArg2);
*/
template<typename TaskType, typename ...TaskArgTypes>
FTaskID AddTask(const FTaskParams& InParams, TaskArgTypes&&... Args)
{
TaskFunctionPtr Function(TInPlaceType<UnboundTaskFunctionPtr>(), TAnonTaskWrapper<TaskType>::Execute);
return AddTask(InParams, MakeShared<TAnonTaskWrapper<TaskType>>(Forward<TaskArgTypes>(Args)...), Function);
}
/**
* Add a new task that calls a member function of the type void (*)()
*
* Example usage:
* TaskScheduler->AddTask(FTaskParams(GET_STAT_ID(StatId)), this, &UMyClass::ResetWeights);
*/
template<typename TaskType>
FTaskID AddMemberFunctionTask(const FTaskParams& InParams, TaskType* Instance, typename TMemberFunctionTaskWrapper<TaskType>::MemberFunctionPtr FunctionPtr)
{
TaskFunctionPtr Function(TInPlaceType<UnboundTaskFunctionPtr>(), TMemberFunctionTaskWrapper<TaskType>::Execute);
return AddTask(InParams, MakeShared<TMemberFunctionTaskWrapper<TaskType>>(Instance, FunctionPtr), Function);
}
/**
* Add a 'null' task that can be used to join many tasks into a single dependency
*/
MOVIESCENE_API FTaskID AddNullTask();
/**
* Add an anonymous unbound task for doing non-ecs work
*/
MOVIESCENE_API FTaskID AddTask(const FTaskParams& InParams, TSharedPtr<ITaskContext> InTaskContext, TaskFunctionPtr InTaskFunction);
/**
* Create one task for each of the entity allocations that match the specified filter
*/
MOVIESCENE_API FTaskID CreateForkedAllocationTask(const FTaskParams& InParams, TSharedPtr<ITaskContext> InTaskContext, TaskFunctionPtr InTaskFunction, TFunctionRef<void(FEntityAllocationIteratorItem,TArray<FPreLockedDataPtr>&)> InPreLockFunc, const FEntityComponentFilter& Filter, const FComponentMask& ReadDeps, const FComponentMask& WriteDeps);
/**
* Define a prerequisite for the given task
*/
MOVIESCENE_API void AddPrerequisite(FTaskID Prerequisite, FTaskID Subsequent);
/**
* Add a child to the front of a previously created 'forked' task. Used for defining 'PreTask' work
*/
MOVIESCENE_API void AddChildBack(FTaskID Parent, FTaskID Child);
/**
* Add a child to the back of a previously created 'forked' task. Used for defining 'PostTask' work
*/
MOVIESCENE_API void AddChildFront(FTaskID Parent, FTaskID Child);
private:
friend class FEntitySystemScheduler;
IEntitySystemScheduler() = default;
};
} // namespace UE::MovieScene