Files
UnrealEngine/Engine/Plugins/Mutable/Source/CustomizableObject/Internal/MuCO/FMutableTaskGraph.h
2025-05-18 13:04:45 +08:00

151 lines
5.2 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "MuR/System.h" // For the MUTABLE_USE_NEW_TASKGRAPH define
#include "Tasks/Task.h"
#include "Async/TaskGraphInterfaces.h"
#include "Containers/Queue.h"
/** Mutable Tasks System.
*
* Allows launching tasks in different threads:
* - Mutable Thread
* - Game Thread
* - Any Thread
*
* Concurrency between tasks in the Mutable Thread is forbidden by chaining all tasks through their prerequisites.
*
*
* LOW PRIORITY MUTABLE TASKS:
*
* - Only one Low Priority task can be launched at the same time.
* This is because once a Task Graph task is launched, it can not be canceled.
* To allow canceling them, the system holds them until it can ensure that its execution will be imminent (no other tasks running).
*
* - A Low Priority task will not be launched if one of the follow conditions is true (in order):
* 1. There is a task Low Priority task running.
* 2. Flag bLaunchMutableTaskLowPriority is false.
* 3. There is a Normal Priority task running (unless time limit).
*/
class FMutableTaskGraph
{
struct FMutableThreadLowPriorityTask
{
uint32 Id;
FString DebugName;
TFunction<void()> Body;
double CreationTime = FPlatformTime::Seconds();
};
struct FGameThreadTask
{
FString DebugName;
TUniqueFunction<void()> Body;
TArray<UE::Tasks::FTask> Prerequisites;
bool bLockMutableThread = false;
/** Check if all the dependencies of this task have been completed. */
bool AreDependenciesComplete() const;
};
public:
FMutableTaskGraph();
~FMutableTaskGraph();
void AddGameThreadTask(const TCHAR* DebugName, TUniqueFunction<void()>&& TaskBody, bool bLockMutableThread = false, const TArray<UE::Tasks::FTask>& Prerequisites = {});
/** Create and launch a task on the Mutable Thread with Normal priority. */
UE::Tasks::FTask AddMutableThreadTask(const TCHAR* DebugName, TUniqueFunction<void()>&& TaskBody, const TArray<UE::Tasks::FTask>& Prerequisites = {});
/** Create and launch a task on the Mutable Thread with Low priority. */
uint32 AddMutableThreadTaskLowPriority(const TCHAR* DebugName, TFunction<void()>&& TaskBody);
/** Cancel, if not already launched, a Mutable Thread with Low priority.
* Return true if the task has been canceled before launching it.
* Return false if not found or running. */
bool CancelMutableThreadTaskLowPriority(uint32 Id);
/** Create and launch a task on Any Thread. */
void AddAnyThreadTask(const TCHAR* DebugName, TUniqueFunction<void()>&& TaskBody) const;
/** Wait for all Mutable Thread tasks. */
void WaitForMutableTasks();
/** Wait for the launched low-priority task if it matches the TaskID. */
void WaitForLaunchedLowPriorityTask(uint32 TaskID);
/** Allow or disallow launching Mutable Tasks with Low priority.
@param bFromMutableTask true if called from a Mutable Task. */
void AllowLaunchingMutableTaskLowPriority(bool bAllow, bool bFromMutableTask);
/** Returns the number of remaining tasks. */
int32 Tick();
private:
/** Lock, as soon as possible, the Mutable Thread. */
void AsyncLockMutableThread();
/** Check if the Mutable Thread is locked. */
bool IsMutableThreadLocked() const;
/** Check if the Mutable Thread is locked without using the lock. Does not update the region. */
bool IsMutableThreadLockedNoLock() const;
public:
/** Unlock the Mutable Thread. Releases all blocked tasks. */
void UnlockMutableThread(); // TODO Make private once UE-281921
private:
/** A Mutable Task Low Priority will only be launched if:
* - No other low priority task is running.
* - Is allowed to launch low priority tasks.
*
* @param bFromMutableTask true if called from a Mutable Task. */
void TryLaunchMutableTaskLowPriority(bool bFromMutableTask);
void TryLaunchGameThreadTask();
/** Return true if the task is completed (or is no longer valid). */
bool IsTaskCompleted(const UE::Tasks::FTask& Task) const;
mutable FCriticalSection MutableTaskLock;
/** Allow or disallow launching low priority tasks. */
bool bAllowLaunchMutableTaskLowPriority = true;
/** Queue of low priority tasks. FIFO. */
TArray<FMutableThreadLowPriorityTask> QueueMutableTasksLowPriority;
/** Queue of Game Thread tasks that need to be executed. */
TQueue<FGameThreadTask> GameThreadTasks;
public:
static constexpr uint32 INVALID_ID = 0;
private:
/** Incremental task ID generator. */
uint32 TaskIdGenerator = INVALID_ID;
/** The ID of the Last Mutable Task Low Priority launched to the TaskGraph system. */
uint32 LastMutableTaskLowPriorityID = INVALID_ID;
/** Last Mutable Task Low Priority launched to the TaskGraph system. */
UE::Tasks::FTask LastMutableTaskLowPriority;
/** Last Mutable Task launched to the TaskGraph system. Low and normal priority. */
UE::Tasks::FTask LastMutableTask;
/** Last Mutable Task launched before locking the Mutable Thread. Once completed the Mutable Thread will be considered locked. */
UE::Tasks::FTask LastMutableTaskBeforeLock;
/** Event to block upcoming tasks when the Mutable Thread is locked.
* Completed = Mutable Thread lock has been requested. May not be locked due to tasks still running (see LastMutableTaskBeforeLock).
* Not Completed = Mutable Thread unlocked. */
UE::Tasks::FTaskEvent MutableThreadUnlockEvent { UE_SOURCE_LOCATION };
};