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

800 lines
18 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Misc/NotNull.h"
#include "MassExecutionContext.h"
#define UE_API MASSENTITY_API
class UMassProcessor;
namespace UE::Mass
{
/** Interface for QueryDefinition templates. Not intended for other direct inheritance. */
struct FQueryDefinitionBase
{
virtual void ConfigureQuery(FMassEntityQuery& EntityQuery, FMassSubsystemRequirements& ProcessorRequirements) = 0;
virtual void SetupForExecute(FMassExecutionContext& Context) = 0;
};
/**
* A MassEntityQuery wrapper with type-safe data access.
*/
struct FQueryExecutor
{
template<typename T, typename = typename TEnableIf<TIsDerivedFrom<T, FQueryExecutor>::IsDerived>::Type>
static TSharedPtr<T> CreateQuery(FMassEntityQuery& InQuery, UObject* InLogOwner = nullptr)
{
TSharedPtr<T> Query = MakeShared<T>();
Query->BoundQuery = &InQuery;
Query->LogOwner = InLogOwner;
#if WITH_MASSENTITY_DEBUG
Query->DebugSize = sizeof(T);
Query->ValidateAccessors();
#endif
return Query;
}
template<typename... Ts>
friend struct FQueryDefinition;
friend ::UMassProcessor;
UE_API explicit FQueryExecutor(FMassEntityQuery& InQuery, UObject* InLogOwner = nullptr);
virtual ~FQueryExecutor() = default;
/** Override with logic to perform against the entities returned by this query. */
virtual void Execute(FMassExecutionContext& Context) = 0;
protected:
FORCEINLINE UObject* GetLogOwner()
{
return LogOwner.Get();
}
template<typename TAccessors, typename TFunc>
FORCEINLINE void ForEachEntityChunk(FMassExecutionContext& ExecutionContext, TAccessors& Accessors, const TFunc&& ExecuteFunction)
{
BoundQuery->ForEachEntityChunk(ExecutionContext, [&Accessors, &ExecuteFunction](FMassExecutionContext& Context)
{
Accessors.AccessorTuple.ApplyBefore([&Context](auto&... Accessor)
{
(Accessor.SetupForChunk(Context), ...);
});
ExecuteFunction(Context, Accessors);
});
}
template<typename TAccessors, typename TFunc>
FORCEINLINE void ParallelForEachEntityChunk(FMassExecutionContext& ExecutionContext, const TAccessors& Accessors, const TFunc&& ExecuteFunction)
{
BoundQuery->ParallelForEachEntityChunk(ExecutionContext, [&Accessors, &ExecuteFunction](FMassExecutionContext& Context)
{
TAccessors LocalAccessors = TAccessors(Accessors);
LocalAccessors.AccessorTuple.ApplyBefore([&Context](auto&... Accessor)
{
(Accessor.SetupForChunk(Context), ...);
});
ExecuteFunction(Context, LocalAccessors);
});
}
template<typename TAccessors, typename TFunc>
FORCEINLINE void ForEachEntity(FMassExecutionContext& ExecutionContext, TAccessors& Accessors, const TFunc&& ExecuteFunction)
{
BoundQuery->ForEachEntityChunk(ExecutionContext, [&Accessors, &ExecuteFunction](FMassExecutionContext& Context)
{
Accessors.AccessorTuple.ApplyBefore([&Context](auto&... Accessor)
{
(Accessor.SetupForChunk(Context), ...);
});
for (uint32 EntityIndex : Context.CreateEntityIterator())
{
ExecuteFunction(Context, Accessors, EntityIndex);
}
});
}
template<typename TAccessors, typename TFunc>
FORCEINLINE void ParallelForEachEntity(FMassExecutionContext& ExecutionContext, TAccessors& Accessors, const TFunc&& ExecuteFunction)
{
BoundQuery->ParallelForEachEntityChunk(ExecutionContext, [&Accessors, &ExecuteFunction](FMassExecutionContext& Context)
{
TAccessors LocalAccessors = TAccessors(Accessors);
LocalAccessors.AccessorTuple.ApplyBefore([&Context](auto&... Accessor)
{
(Accessor.SetupForChunk(Context), ...);
});
for (uint32 EntityIndex : Context.CreateEntityIterator())
{
ExecuteFunction(Context, LocalAccessors, EntityIndex);
}
});
}
UE_API FQueryExecutor();
private:
static UE_API FMassEntityQuery DummyQuery;
UE_API void ConfigureQuery(FMassSubsystemRequirements& ProcessorRequirements);
UE_API void CallExecute(FMassExecutionContext& Context);
TNotNull<FMassEntityQuery*> BoundQuery;
TWeakObjectPtr<UObject> LogOwner;
/** AccessorsPtr is only allowed to point to a member variable of this struct and will assert in all other cases. */
FQueryDefinitionBase* AccessorsPtr;
#if WITH_MASSENTITY_DEBUG
UE_API void ValidateAccessors();
uint32 DebugSize = 0;
#endif
};
template <typename...>
inline constexpr auto IsUnique = std::true_type{};
template <typename T, typename... Rest>
inline constexpr auto IsUnique<T, Rest...> = std::bool_constant<(!std::is_same_v<typename T::FragmentType, typename Rest::FragmentType> && ...) && IsUnique<Rest...>>{};
template <typename T, typename U, typename... Us>
constexpr auto GetIndexRecursive()
{
if constexpr (std::is_same<T, typename U::FragmentType>::value)
{
return 0;
}
else
{
static_assert(sizeof...(Us) > 0, "Type not found in accessor collection.");
return 1 + GetIndexRecursive<T, Us...>();
}
}
template <typename T, typename U, typename... Us>
constexpr auto GetAccessorIndex()
{
static_assert(IsUnique<U, Us...>, "An accessor collection must only contain a single instance of each fragment/subsystem/tag type.");
return GetIndexRecursive<T, U, Us...>();
}
/**
* Defines the entity compositions to return in the query and provides type-safe data access to
* entity and subsystem data. Must be a member variable of a QueryExecutor
*/
template<typename... Accessors>
struct FQueryDefinition : public FQueryDefinitionBase
{
FQueryDefinition(FQueryExecutor& Owner)
{
Owner.AccessorsPtr = this;
}
TTuple<Accessors...> AccessorTuple{};
virtual void ConfigureQuery(FMassEntityQuery& EntityQuery, FMassSubsystemRequirements& ProcessorRequirements) override
{
AccessorTuple.ApplyBefore([&EntityQuery, &ProcessorRequirements](auto&... Accessor)
{
(Accessor.ConfigureQuery(EntityQuery, ProcessorRequirements), ...);
});
}
virtual void SetupForExecute(FMassExecutionContext& Context) override
{
AccessorTuple.ApplyBefore([&Context](auto&... Accessor)
{
(Accessor.SetupForExecute(Context), ...);
});
}
FORCEINLINE void SetupForChunk(FMassExecutionContext& Context)
{
AccessorTuple.ApplyBefore([&Context](auto&... Accessor)
{
(Accessor.SetupForChunk(Context), ...);
});
}
template <typename TFragment>
FORCEINLINE constexpr auto& Get()
{
constexpr std::size_t Index = GetAccessorIndex<TFragment, Accessors...>();
return AccessorTuple.template Get<Index>();
}
};
template<typename TFragment>
struct FMutableFragmentAccess
{
template<typename... Ts>
friend struct FQueryDefinition;
using FragmentType = TFragment;
FMutableFragmentAccess() = default;
FORCEINLINE TArrayView<TFragment>& Get()
{
return View;
}
FORCEINLINE TFragment& operator[](int32 Index)
{
return View[Index];
}
FORCEINLINE void ConfigureQuery(FMassEntityQuery& EntityQuery, FMassSubsystemRequirements& ProcessorRequirements)
{
EntityQuery.AddRequirement<TFragment>(EMassFragmentAccess::ReadWrite);
}
FORCEINLINE void SetupForExecute(FMassExecutionContext& Context)
{
}
FORCEINLINE void SetupForChunk(FMassExecutionContext& Context)
{
View = Context.GetMutableFragmentView<TFragment>();
}
TArrayView<TFragment> View;
};
template<typename TFragment>
struct FConstFragmentAccess
{
template<typename... Ts>
friend struct FQueryDefinition;
using FragmentType = TFragment;
FConstFragmentAccess() = default;
FORCEINLINE const TConstArrayView<TFragment>& Get() const
{
return View;
}
FORCEINLINE const TFragment& operator[](int32 Index) const
{
return View[Index];
}
FORCEINLINE void ConfigureQuery(FMassEntityQuery& EntityQuery, FMassSubsystemRequirements& ProcessorRequirements) const
{
EntityQuery.AddRequirement<TFragment>(EMassFragmentAccess::ReadOnly);
}
FORCEINLINE void SetupForExecute(FMassExecutionContext& Context)
{
}
FORCEINLINE void SetupForChunk(FMassExecutionContext& Context)
{
View = Context.GetFragmentView<TFragment>();
}
TConstArrayView<TFragment> View;
};
template<typename TFragment>
struct FMutableOptionalFragmentAccess
{
template<typename... Ts>
friend struct FQueryDefinition;
using FragmentType = TFragment;
FMutableOptionalFragmentAccess() = default;
FORCEINLINE TArrayView<TFragment>& Get() const
{
return View;
}
FORCEINLINE TFragment& operator[](int32 Index) const
{
return View[Index];
}
FORCEINLINE int32 Num() const
{
return View.Num();
}
FORCEINLINE operator bool() const
{
return View.Num() > 0;
}
FORCEINLINE void ConfigureQuery(FMassEntityQuery& EntityQuery, FMassSubsystemRequirements& ProcessorRequirements) const
{
EntityQuery.AddRequirement<TFragment>(EMassFragmentAccess::ReadWrite, EMassFragmentPresence::Optional);
}
FORCEINLINE void SetupForExecute(FMassExecutionContext& Context)
{
}
FORCEINLINE void SetupForChunk(FMassExecutionContext& Context)
{
View = Context.GetMutableFragmentView<TFragment>();
}
TArrayView<TFragment> View;
};
template<typename TFragment>
struct FConstOptionalFragmentAccess
{
template<typename... Ts>
friend struct FQueryDefinition;
using FragmentType = TFragment;
FConstOptionalFragmentAccess() = default;
FORCEINLINE const TConstArrayView<TFragment>& Get() const
{
return View;
}
FORCEINLINE const TFragment& operator[](int32 Index) const
{
return View[Index];
}
FORCEINLINE int32 Num() const
{
return View.Num();
}
FORCEINLINE operator bool() const
{
return View.Num() > 0;
}
FORCEINLINE void ConfigureQuery(FMassEntityQuery& EntityQuery, FMassSubsystemRequirements& ProcessorRequirements) const
{
EntityQuery.AddRequirement<TFragment>(EMassFragmentAccess::ReadOnly);
}
FORCEINLINE void SetupForExecute(FMassExecutionContext& Context)
{
}
FORCEINLINE void SetupForChunk(FMassExecutionContext& Context)
{
View = Context.GetFragmentView<TFragment>();
}
TConstArrayView<TFragment> View;
};
template<typename TTag>
struct FMassTagRequired
{
template<typename... Ts>
friend struct FQueryDefinition;
using FragmentType = TTag;
FMassTagRequired() = default;
FORCEINLINE void ConfigureQuery(FMassEntityQuery& EntityQuery, FMassSubsystemRequirements& ProcessorRequirements) const
{
EntityQuery.AddTagRequirement<TTag>(EMassFragmentPresence::All);
}
FORCEINLINE void SetupForExecute(FMassExecutionContext& Context)
{
}
FORCEINLINE void SetupForChunk(FMassExecutionContext& Context)
{
}
};
template<typename TFragment>
struct FMassTagBlocked
{
using FragmentType = TFragment;
FORCEINLINE void ConfigureQuery(FMassEntityQuery& EntityQuery, FMassSubsystemRequirements& ProcessorRequirements) const
{
EntityQuery.AddTagRequirement<TFragment>(EMassFragmentPresence::None);
}
FORCEINLINE void SetupForExecute(FMassExecutionContext& Context)
{
}
FORCEINLINE void SetupForChunk(FMassExecutionContext& Context)
{
}
};
template<typename TFragment>
struct FMutableSharedFragmentAccess
{
template<typename... Ts>
friend struct FQueryDefinition;
using FragmentType = TFragment;
FMutableSharedFragmentAccess() = default;
FORCEINLINE TFragment& Get() const
{
check(Fragment);
return *Fragment;
}
FORCEINLINE TFragment& operator*() const
{
check(Fragment);
return *Fragment;
}
FORCEINLINE TFragment* operator->() const
{
return Fragment;
}
FORCEINLINE void ConfigureQuery(FMassEntityQuery& EntityQuery, FMassSubsystemRequirements& ProcessorRequirements) const
{
EntityQuery.AddSharedRequirement<TFragment>(EMassFragmentAccess::ReadWrite);
}
FORCEINLINE void SetupForExecute(FMassExecutionContext& Context)
{
}
FORCEINLINE void SetupForChunk(FMassExecutionContext& Context)
{
Fragment = &(Context.GetSharedFragment<TFragment>());
}
TFragment* Fragment = nullptr;
};
template<typename TFragment>
struct FConstSharedFragmentAccess
{
template<typename... Ts>
friend struct FQueryDefinition;
using FragmentType = TFragment;
FConstSharedFragmentAccess() = default;
FORCEINLINE const TFragment& Get() const
{
check(Fragment);
return *Fragment;
}
FORCEINLINE const TFragment& operator*() const
{
check(Fragment);
return *Fragment;
}
FORCEINLINE const TFragment* operator->() const
{
return Fragment;
}
FORCEINLINE void ConfigureQuery(FMassEntityQuery& EntityQuery, FMassSubsystemRequirements& ProcessorRequirements) const
{
EntityQuery.AddConstSharedRequirement<TFragment>();
}
FORCEINLINE void SetupForExecute(FMassExecutionContext& Context)
{
}
FORCEINLINE void SetupForChunk(FMassExecutionContext& Context)
{
Fragment = &Context.GetConstSharedFragment<TFragment>();
}
const TFragment* Fragment = nullptr;
};
template<typename TFragment>
struct FMutableChunkFragmentAccess
{
template<typename... Ts>
friend struct FQueryDefinition;
using FragmentType = TFragment;
FMutableChunkFragmentAccess() = default;
FORCEINLINE TFragment& Get() const
{
check(Fragment);
return *Fragment;
}
FORCEINLINE TFragment& operator*() const
{
check(Fragment);
return *Fragment;
}
FORCEINLINE TFragment* operator->() const
{
return Fragment;
}
FORCEINLINE void ConfigureQuery(FMassEntityQuery& EntityQuery, FMassSubsystemRequirements& ProcessorRequirements) const
{
EntityQuery.AddChunkRequirement<TFragment>(EMassFragmentAccess::ReadWrite, EMassFragmentPresence::All);
}
FORCEINLINE void SetupForExecute(FMassExecutionContext& Context)
{
}
FORCEINLINE void SetupForChunk(FMassExecutionContext& Context)
{
Fragment = Context.GetMutableChunkFragmentPtr<TFragment>();
}
TFragment* Fragment = nullptr;
};
template<typename TFragment>
struct FConstChunkFragmentAccess
{
template<typename... Ts>
friend struct FQueryDefinition;
using FragmentType = TFragment;
FConstChunkFragmentAccess() = default;
FORCEINLINE const TFragment& Get() const
{
check(Fragment);
return *Fragment;
}
FORCEINLINE const TFragment& operator*() const
{
check(Fragment);
return *Fragment;
}
FORCEINLINE const TFragment* operator->() const
{
return Fragment;
}
FORCEINLINE void ConfigureQuery(FMassEntityQuery& EntityQuery, FMassSubsystemRequirements& ProcessorRequirements) const
{
EntityQuery.AddChunkRequirement<TFragment>(EMassFragmentAccess::ReadOnly, EMassFragmentPresence::All);
}
FORCEINLINE void SetupForExecute(FMassExecutionContext& Context)
{
}
FORCEINLINE void SetupForChunk(FMassExecutionContext& Context)
{
Fragment = Context.GetChunkFragmentPtr<TFragment>();
}
const TFragment* Fragment = nullptr;
};
template<typename TFragment>
struct FMutableOptionalChunkFragmentAccess
{
template<typename... Ts>
friend struct FQueryDefinition;
using FragmentType = TFragment;
FMutableOptionalChunkFragmentAccess() = default;
FORCEINLINE TFragment* Get() const
{
check(Fragment);
return Fragment;
}
FORCEINLINE TFragment& operator*() const
{
check(Fragment);
return *Fragment;
}
FORCEINLINE TFragment* operator->() const
{
return Fragment;
}
FORCEINLINE operator bool() { return Fragment != nullptr; }
FORCEINLINE void ConfigureQuery(FMassEntityQuery& EntityQuery, FMassSubsystemRequirements& ProcessorRequirements) const
{
EntityQuery.AddChunkRequirement<TFragment>(EMassFragmentAccess::ReadWrite, EMassFragmentPresence::Optional);
}
FORCEINLINE void SetupForExecute(FMassExecutionContext& Context)
{
}
FORCEINLINE void SetupForChunk(FMassExecutionContext& Context)
{
Fragment = Context.GetMutableChunkFragmentPtr<TFragment>();
}
TFragment* Fragment = nullptr;
};
template<typename TFragment>
struct FConstOptionalChunkFragmentAccess
{
template<typename... Ts>
friend struct FQueryDefinition;
using FragmentType = TFragment;
FConstOptionalChunkFragmentAccess() = default;
FORCEINLINE const TFragment* Get()
{
check(Fragment);
return Fragment;
}
FORCEINLINE const TFragment& operator*() const
{
check(Fragment);
return *Fragment;
}
FORCEINLINE const TFragment* operator->() const
{
return Fragment;
}
FORCEINLINE operator bool() const
{
return Fragment != nullptr;
}
FORCEINLINE void ConfigureQuery(FMassEntityQuery& EntityQuery, FMassSubsystemRequirements& ProcessorRequirements) const
{
EntityQuery.AddChunkRequirement<TFragment>(EMassFragmentAccess::ReadOnly, EMassFragmentPresence::Optional);
}
FORCEINLINE void SetupForExecute(FMassExecutionContext& Context)
{
}
FORCEINLINE void SetupForChunk(FMassExecutionContext& Context)
{
Fragment = Context.GetChunkFragmentPtr<TFragment>();
}
const TFragment* Fragment = nullptr;
};
template<typename TSubsystem, typename = typename TEnableIf<TIsDerivedFrom<TSubsystem, USubsystem>::IsDerived>::Type>
struct FMutableSubsystemAccess
{
template<typename... Ts>
friend struct FQueryDefinition;
using FragmentType = TSubsystem;
FMutableSubsystemAccess() = default;
FORCEINLINE TSubsystem* Get() const
{
return Subsystem;
}
FORCEINLINE TSubsystem& operator*() const
{
check(Subsystem);
return *Subsystem;
}
FORCEINLINE TSubsystem* operator->() const
{
return Subsystem;
}
FORCEINLINE operator bool() const
{
return Subsystem != nullptr;
}
FORCEINLINE void ConfigureQuery(FMassEntityQuery& EntityQuery, FMassSubsystemRequirements& ProcessorRequirements) const
{
ProcessorRequirements.AddSubsystemRequirement<TSubsystem>(EMassFragmentAccess::ReadWrite);
}
FORCEINLINE void SetupForExecute(FMassExecutionContext& Context)
{
Subsystem = Context.GetMutableSubsystem<TSubsystem>();
}
FORCEINLINE void SetupForChunk(FMassExecutionContext& Context)
{
}
TSubsystem* Subsystem = nullptr;
};
template<typename TSubsystem, typename = typename TEnableIf<TIsDerivedFrom<TSubsystem, USubsystem>::IsDerived>::Type>
struct FConstSubsystemAccess
{
template<typename... Ts>
friend struct FQueryDefinition;
using FragmentType = TSubsystem;
FConstSubsystemAccess() = default;
FORCEINLINE const TSubsystem* Get() const
{
return Subsystem;
}
FORCEINLINE const TSubsystem& operator*() const
{
check(Subsystem);
return *Subsystem;
}
FORCEINLINE const TSubsystem* operator->() const
{
return Subsystem;
}
FORCEINLINE operator bool() const
{
return Subsystem != nullptr;
}
FORCEINLINE void ConfigureQuery(FMassEntityQuery& EntityQuery, FMassSubsystemRequirements& ProcessorRequirements) const
{
ProcessorRequirements.AddSubsystemRequirement<TSubsystem>(EMassFragmentAccess::ReadOnly);
}
FORCEINLINE void SetupForExecute(FMassExecutionContext& Context)
{
Subsystem = Context.GetSubsystem(TSubsystem::StaticClass());
}
FORCEINLINE void SetupForChunk(FMassExecutionContext& Context)
{
}
const TSubsystem* Subsystem = nullptr;
};
} // namespace UE::Mass
#undef UE_API