// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "Misc/NotNull.h" #include "MassExecutionContext.h" 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 MASSENTITY_API FQueryExecutor { template::IsDerived>::Type> static TSharedPtr CreateQuery(FMassEntityQuery& InQuery, UObject* InLogOwner = nullptr) { TSharedPtr Query = MakeShared(); Query->BoundQuery = &InQuery; Query->LogOwner = InLogOwner; #if WITH_MASSENTITY_DEBUG Query->DebugSize = sizeof(T); Query->ValidateAccessors(); #endif return Query; } template friend struct FQueryDefinition; friend ::UMassProcessor; 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 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 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 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 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); } }); } FQueryExecutor(); private: static FMassEntityQuery DummyQuery; void ConfigureQuery(FMassSubsystemRequirements& ProcessorRequirements); void CallExecute(FMassExecutionContext& Context); TNotNull BoundQuery; TWeakObjectPtr 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 void ValidateAccessors(); uint32 DebugSize = 0; #endif }; template inline constexpr auto IsUnique = std::true_type{}; template inline constexpr auto IsUnique = std::bool_constant<(!std::is_same_v && ...) && IsUnique>{}; template constexpr auto GetIndexRecursive() { if constexpr (std::is_same::value) { return 0; } else { static_assert(sizeof...(Us) > 0, "Type not found in accessor collection."); return 1 + GetIndexRecursive(); } } template constexpr auto GetAccessorIndex() { static_assert(IsUnique, "An accessor collection must only contain a single instance of each fragment/subsystem/tag type."); return GetIndexRecursive(); } /** * 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 struct FQueryDefinition : public FQueryDefinitionBase { FQueryDefinition(FQueryExecutor& Owner) { Owner.AccessorsPtr = this; } TTuple 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 FORCEINLINE constexpr auto& Get() { constexpr std::size_t Index = GetAccessorIndex(); return AccessorTuple.template Get(); } }; template struct FMutableFragmentAccess { template friend struct FQueryDefinition; using FragmentType = TFragment; FMutableFragmentAccess() = default; FORCEINLINE TArrayView& Get() { return View; } FORCEINLINE TFragment& operator[](int32 Index) { return View[Index]; } FORCEINLINE void ConfigureQuery(FMassEntityQuery& EntityQuery, FMassSubsystemRequirements& ProcessorRequirements) { EntityQuery.AddRequirement(EMassFragmentAccess::ReadWrite); } FORCEINLINE void SetupForExecute(FMassExecutionContext& Context) { } FORCEINLINE void SetupForChunk(FMassExecutionContext& Context) { View = Context.GetMutableFragmentView(); } TArrayView View; }; template struct FConstFragmentAccess { template friend struct FQueryDefinition; using FragmentType = TFragment; FConstFragmentAccess() = default; FORCEINLINE const TConstArrayView& Get() const { return View; } FORCEINLINE const TFragment& operator[](int32 Index) const { return View[Index]; } FORCEINLINE void ConfigureQuery(FMassEntityQuery& EntityQuery, FMassSubsystemRequirements& ProcessorRequirements) const { EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); } FORCEINLINE void SetupForExecute(FMassExecutionContext& Context) { } FORCEINLINE void SetupForChunk(FMassExecutionContext& Context) { View = Context.GetFragmentView(); } TConstArrayView View; }; template struct FMutableOptionalFragmentAccess { template friend struct FQueryDefinition; using FragmentType = TFragment; FMutableOptionalFragmentAccess() = default; FORCEINLINE TArrayView& 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(EMassFragmentAccess::ReadWrite, EMassFragmentPresence::Optional); } FORCEINLINE void SetupForExecute(FMassExecutionContext& Context) { } FORCEINLINE void SetupForChunk(FMassExecutionContext& Context) { View = Context.GetMutableFragmentView(); } TArrayView View; }; template struct FConstOptionalFragmentAccess { template friend struct FQueryDefinition; using FragmentType = TFragment; FConstOptionalFragmentAccess() = default; FORCEINLINE const TConstArrayView& 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(EMassFragmentAccess::ReadOnly); } FORCEINLINE void SetupForExecute(FMassExecutionContext& Context) { } FORCEINLINE void SetupForChunk(FMassExecutionContext& Context) { View = Context.GetFragmentView(); } TConstArrayView View; }; template struct FMassTagRequired { template friend struct FQueryDefinition; using FragmentType = TTag; FMassTagRequired() = default; FORCEINLINE void ConfigureQuery(FMassEntityQuery& EntityQuery, FMassSubsystemRequirements& ProcessorRequirements) const { EntityQuery.AddTagRequirement(EMassFragmentPresence::All); } FORCEINLINE void SetupForExecute(FMassExecutionContext& Context) { } FORCEINLINE void SetupForChunk(FMassExecutionContext& Context) { } }; template struct FMassTagBlocked { using FragmentType = TFragment; FORCEINLINE void ConfigureQuery(FMassEntityQuery& EntityQuery, FMassSubsystemRequirements& ProcessorRequirements) const { EntityQuery.AddTagRequirement(EMassFragmentPresence::None); } FORCEINLINE void SetupForExecute(FMassExecutionContext& Context) { } FORCEINLINE void SetupForChunk(FMassExecutionContext& Context) { } }; template struct FMutableSharedFragmentAccess { template 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(EMassFragmentAccess::ReadWrite); } FORCEINLINE void SetupForExecute(FMassExecutionContext& Context) { } FORCEINLINE void SetupForChunk(FMassExecutionContext& Context) { Fragment = &(Context.GetSharedFragment()); } TFragment* Fragment = nullptr; }; template struct FConstSharedFragmentAccess { template 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(); } FORCEINLINE void SetupForExecute(FMassExecutionContext& Context) { } FORCEINLINE void SetupForChunk(FMassExecutionContext& Context) { Fragment = &Context.GetConstSharedFragment(); } const TFragment* Fragment = nullptr; }; template struct FMutableChunkFragmentAccess { template 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(EMassFragmentAccess::ReadWrite, EMassFragmentPresence::All); } FORCEINLINE void SetupForExecute(FMassExecutionContext& Context) { } FORCEINLINE void SetupForChunk(FMassExecutionContext& Context) { Fragment = Context.GetMutableChunkFragmentPtr(); } TFragment* Fragment = nullptr; }; template struct FConstChunkFragmentAccess { template 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(EMassFragmentAccess::ReadOnly, EMassFragmentPresence::All); } FORCEINLINE void SetupForExecute(FMassExecutionContext& Context) { } FORCEINLINE void SetupForChunk(FMassExecutionContext& Context) { Fragment = Context.GetChunkFragmentPtr(); } const TFragment* Fragment = nullptr; }; template struct FMutableOptionalChunkFragmentAccess { template 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(EMassFragmentAccess::ReadWrite, EMassFragmentPresence::Optional); } FORCEINLINE void SetupForExecute(FMassExecutionContext& Context) { } FORCEINLINE void SetupForChunk(FMassExecutionContext& Context) { Fragment = Context.GetMutableChunkFragmentPtr(); } TFragment* Fragment = nullptr; }; template struct FConstOptionalChunkFragmentAccess { template 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(EMassFragmentAccess::ReadOnly, EMassFragmentPresence::Optional); } FORCEINLINE void SetupForExecute(FMassExecutionContext& Context) { } FORCEINLINE void SetupForChunk(FMassExecutionContext& Context) { Fragment = Context.GetChunkFragmentPtr(); } const TFragment* Fragment = nullptr; }; template::IsDerived>::Type> struct FMutableSubsystemAccess { template 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(EMassFragmentAccess::ReadWrite); } FORCEINLINE void SetupForExecute(FMassExecutionContext& Context) { Subsystem = Context.GetMutableSubsystem(); } FORCEINLINE void SetupForChunk(FMassExecutionContext& Context) { } TSubsystem* Subsystem = nullptr; }; template::IsDerived>::Type> struct FConstSubsystemAccess { template 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(EMassFragmentAccess::ReadOnly); } FORCEINLINE void SetupForExecute(FMassExecutionContext& Context) { Subsystem = Context.GetSubsystem(TSubsystem::StaticClass()); } FORCEINLINE void SetupForChunk(FMassExecutionContext& Context) { } const TSubsystem* Subsystem = nullptr; }; } // namespace UE::Mass