UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
TaskDelegate.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
6#include "Misc/ScopeExit.h"
7#include "Misc/Launder.h"
8#include <type_traits>
9
10namespace LowLevelTasks
11{
13 {
14 static constexpr uint32 BlockSize = 64 * 1024;
15 static constexpr bool AllowOversizedBlocks = true;
16 static constexpr bool RequiresAccurateSize = false;
17 static constexpr bool InlineBlockAllocation = true;
18 static constexpr const char* TagName = "LowLevelTasksLinear";
19
21 };
22
23 namespace TTaskDelegate_Impl
24 {
25 template<typename ReturnType>
26 inline ReturnType MakeDummyValue()
27 {
28 return *(reinterpret_cast<ReturnType*>(uintptr_t(1)));
29 }
30
31 template<>
33 {
34 return;
35 }
36 }
37 //version of TUniqueFunction<ReturnType()> that is less wasteful with it's memory
38 //this class might be removed when TUniqueFunction<ReturnType()> is fixed
39 template<typename = void(), uint32 = PLATFORM_CACHE_LINE_SIZE>
41
42 template<uint32 TotalSize , typename ReturnType, typename... ParamTypes>
43 class alignas(8) TTaskDelegate<ReturnType(ParamTypes...), TotalSize>
44 {
45 template<typename, uint32>
46 friend class TTaskDelegate;
47
48 using ThisClass = TTaskDelegate<ReturnType(ParamTypes...), TotalSize>;
50 {
51 virtual void Move(TTaskDelegateBase&, void*, void*, uint32)
52 {
53 checkNoEntry(); //if we get to this place than the compiler is optimizing our vtable lookup and ignores our undefined/shallow copy of the memory
54 };
55
56 virtual ReturnType Call(void*, ParamTypes...) const
57 {
58 checkNoEntry(); //if we get to this place than the compiler is optimizing our vtable lookup and ignores our undefined/shallow copy of the memory
59 return TTaskDelegate_Impl::MakeDummyValue<ReturnType>();
60 };
61
62 virtual ReturnType CallAndMove(ThisClass&, void*, uint32, ParamTypes...)
63 {
64 checkNoEntry(); //if we get to this place than the compiler is optimizing our vtable lookup and ignores our undefined/shallow copy of the memory
65 return TTaskDelegate_Impl::MakeDummyValue<ReturnType>();
66 };
67
68 virtual void Destroy(void*)
69 {
70 checkNoEntry(); //if we get to this place than the compiler is optimizing our vtable lookup and ignores our undefined/shallow copy of the memory
71 };
72
73 virtual bool IsHeapAllocated() const
74 {
75 checkNoEntry(); //if we get to this place than the compiler is optimizing our vtable lookup and ignores our undefined/shallow copy of the memory
76 return false;
77 }
78
79 virtual bool IsSet() const
80 {
81 checkNoEntry(); //if we get to this place than the compiler is optimizing our vtable lookup and ignores our undefined/shallow copy of the memory
82 return false;
83 }
84
85 virtual uint32 DelegateSize() const
86 {
87 checkNoEntry(); //if we get to this place than the compiler is optimizing our vtable lookup and ignores our undefined/shallow copy of the memory
88 return 0;
89 }
90 };
91
93 {
94 void Move(TTaskDelegateBase&, void*, void*, uint32) override
95 {
96 }
97
98 ReturnType Call(void*, ParamTypes...) const override
99 {
100 checkf(false, TEXT("trying to Call a dummy TaskDelegate"));
101 return TTaskDelegate_Impl::MakeDummyValue<ReturnType>();
102 }
103
104 ReturnType CallAndMove(ThisClass&, void*, uint32, ParamTypes...) override
105 {
106 checkf(false, TEXT("trying to Call a dummy TaskDelegate"));
107 return TTaskDelegate_Impl::MakeDummyValue<ReturnType>();
108 }
109
110 void Destroy(void*) override
111 {
112 }
113
114 bool IsHeapAllocated() const override
115 {
116 return false;
117 }
118
119 bool IsSet() const override
120 {
121 return false;
122 }
123
124 uint32 DelegateSize() const override
125 {
126 return 0;
127 }
128 };
129
130 template<typename TCallableType, bool HeapAllocated>
131 struct TTaskDelegateImpl;
132
133 template<typename TCallableType>
134 struct TTaskDelegateImpl<TCallableType, false> final : TTaskDelegateBase
135 {
136 template<typename CallableT>
137 inline TTaskDelegateImpl(CallableT&& Callable, void* InlineData)
138 {
139 static_assert(TIsInvocable<TCallableType, ParamTypes...>::Value, "TCallableType is not invocable");
140 static_assert(std::is_same_v<ReturnType, decltype(Callable(UE::Core::Private::IsInvocable::DeclVal<ParamTypes>()...))>, "TCallableType return type does not match");
141 static_assert(sizeof(TTaskDelegateImpl<TCallableType, false>) == sizeof(TTaskDelegateBase), "Size must match the Baseclass");
142 new (InlineData) TCallableType(Forward<CallableT>(Callable));
143 }
144
145 inline void Move(TTaskDelegateBase& DstWrapper, void* DstData, void* SrcData, uint32 DestInlineSize) override
146 {
147 TCallableType* SrcPtr = reinterpret_cast<TCallableType*>(SrcData);
148 if ((sizeof(TCallableType) <= DestInlineSize) && (uintptr_t(DstData) % alignof(TCallableType)) == 0)
149 {
151 }
152 else
153 {
155 }
156 new (this) TTaskDelegateDummy();
157 }
158
159 inline ReturnType Call(void* InlineData, ParamTypes... Params) const override
160 {
161 TCallableType* LocalPtr = reinterpret_cast<TCallableType*>(InlineData);
162 return Invoke(*LocalPtr, Params...);
163 }
164
165 ReturnType CallAndMove(ThisClass& Destination, void* InlineData, uint32 DestInlineSize, ParamTypes... Params) override
166 {
168 {
169 Move(Destination.CallableWrapper, Destination.InlineStorage, InlineData, DestInlineSize);
170 };
171 return Call(InlineData, Params...);
172 }
173
174 void Destroy(void* InlineData) override
175 {
176 TCallableType* LocalPtr = reinterpret_cast<TCallableType*>(InlineData);
177 LocalPtr->~TCallableType();
178 }
179
180 bool IsHeapAllocated() const override
181 {
182 return false;
183 }
184
185 bool IsSet() const override
186 {
187 return true;
188 }
189
190 uint32 DelegateSize() const override
191 {
192 return sizeof(TCallableType);
193 }
194 };
195
196 template<typename TCallableType>
197 struct TTaskDelegateImpl<TCallableType, true> final : TTaskDelegateBase
198 {
199 private:
200 inline TTaskDelegateImpl(void* DstData, void* SrcData)
201 {
202 memcpy(DstData, SrcData, sizeof(TCallableType*));
203 }
204
205 public:
206 template<typename CallableT>
207 inline TTaskDelegateImpl(CallableT&& Callable, void* InlineData)
208 {
209 static_assert(TIsInvocable<TCallableType, ParamTypes...>::Value, "TCallableType is not invocable");
210 static_assert(std::is_same_v<ReturnType, decltype(Callable(UE::Core::Private::IsInvocable::DeclVal<ParamTypes>()...))>, "TCallableType return type does not match");
211 static_assert(sizeof(TTaskDelegateImpl<TCallableType, true>) == sizeof(TTaskDelegateBase), "Size must match the Baseclass");
212 static_assert(alignof(TCallableType) <= FLowLevelTasksBlockAllocationTag::Allocator::MaxAlignment);
213 TCallableType** HeapPtr = reinterpret_cast<TCallableType**>(InlineData);
215 new (*HeapPtr) TCallableType(Forward<CallableT>(Callable));
216 }
217
218 inline void Move(TTaskDelegateBase& DstWrapper, void* DstData, void* SrcData, uint32 DestInlineSize) override
219 {
221 new (this) TTaskDelegateDummy();
222 }
223
224 inline ReturnType Call(void* InlineData, ParamTypes... Params) const override
225 {
226 TCallableType* HeapPtr = reinterpret_cast<TCallableType*>(*reinterpret_cast<void* const*>(InlineData));
227 return Invoke(*HeapPtr, Params...);
228 }
229
230 ReturnType CallAndMove(ThisClass& Destination, void* InlineData, uint32 DestInlineSize, ParamTypes... Params) override
231 {
233 {
234 Move(Destination.CallableWrapper, Destination.InlineStorage, InlineData, DestInlineSize);
235 };
236 return Call(InlineData, Params...);
237 }
238
239 void Destroy(void* InlineData) override
240 {
241 TCallableType* HeapPtr = reinterpret_cast<TCallableType*>(*reinterpret_cast<void**>(InlineData));
242 // We need a typedef here because VC won't compile the destructor call below if ElementType itself has a member called ElementType
244 HeapPtr->DestructorType::~TCallableType();
246 }
247
248 bool IsHeapAllocated() const override
249 {
250 return true;
251 }
252
253 bool IsSet() const override
254 {
255 return true;
256 }
257
258 uint32 DelegateSize() const override
259 {
260 return sizeof(TCallableType);
261 }
262 };
263
264 public:
266 {
267 static_assert(TotalSize % 8 == 0, "Totalsize must be dividable by 8");
268 static_assert(TotalSize >= (sizeof(TTaskDelegateBase) + sizeof(void*)), "Totalsize must be large enough to fit a vtable and pointer");
269 new (&CallableWrapper) TTaskDelegateDummy();
270 }
271
272 template<uint32 SourceTotalSize>
273 TTaskDelegate(const TTaskDelegate<ReturnType(ParamTypes...), SourceTotalSize>&) = delete;
274
275 template<uint32 SourceTotalSize>
276 TTaskDelegate(TTaskDelegate<ReturnType(ParamTypes...), SourceTotalSize>&& Other)
277 {
278 Other.GetWrapper()->Move(CallableWrapper, InlineStorage, Other.InlineStorage, InlineStorageSize);
279 }
280
281 template<typename CallableT>
283 {
284 using TCallableType = std::decay_t<CallableT>;
285 if constexpr ((sizeof(TCallableType) <= InlineStorageSize) && ((uintptr_t(InlineStorageSize) % alignof(TCallableType)) == 0))
286 {
287 new (&CallableWrapper) TTaskDelegateImpl<TCallableType, false>(Forward<CallableT>(Callable), InlineStorage);
288 }
289 else
290 {
291 new (&CallableWrapper) TTaskDelegateImpl<TCallableType, true>(Forward<CallableT>(Callable), InlineStorage);
292 }
293 }
294
296 {
297 GetWrapper()->Destroy(InlineStorage);
298 }
299
300 ReturnType operator()(ParamTypes... Params) const
301 {
302 return GetWrapper()->Call(InlineStorage, Params...);
303 }
304
305 template<uint32 DestTotalSize>
306 ReturnType CallAndMove(TTaskDelegate<ReturnType(ParamTypes...), DestTotalSize>& Destination, ParamTypes... Params)
307 {
308 checkSlow(!Destination.IsSet());
309 return GetWrapper()->CallAndMove(Destination, InlineStorage, TTaskDelegate<ReturnType(ParamTypes...), DestTotalSize>::InlineStorageSize, Params...);
310 }
311
312 template<uint32 SourceTotalSize>
313 ThisClass& operator= (const TTaskDelegate<ReturnType(ParamTypes...), SourceTotalSize>&) = delete;
314
315 template<uint32 SourceTotalSize>
316 ThisClass& operator= (TTaskDelegate<ReturnType(ParamTypes...), SourceTotalSize>&& Other)
317 {
318 GetWrapper()->Destroy(InlineStorage);
319 Other.GetWrapper()->Move(CallableWrapper, InlineStorage, Other.InlineStorage, InlineStorageSize);
320 return *this;
321 }
322
323 template<typename CallableT>
324 ThisClass& operator= (CallableT&& Callable)
325 {
326 using TCallableType = std::decay_t<CallableT>;
327 GetWrapper()->Destroy(InlineStorage);
328 if constexpr ((sizeof(TCallableType) <= InlineStorageSize) && ((uintptr_t(InlineStorageSize) % alignof(TCallableType)) == 0))
329 {
330 new (&CallableWrapper) TTaskDelegateImpl<TCallableType, false>(Forward<CallableT>(Callable), InlineStorage);
331 }
332 else
333 {
334 new (&CallableWrapper) TTaskDelegateImpl<TCallableType, true>(Forward<CallableT>(Callable), InlineStorage);
335 }
336 return *this;
337 }
338
339 void Destroy()
340 {
341 GetWrapper()->Destroy(InlineStorage);
342 new (&CallableWrapper) TTaskDelegateDummy();
343 }
344
345 bool IsHeapAllocated() const
346 {
347 return GetWrapper()->IsHeapAllocated();
348 }
349
350 bool IsSet() const
351 {
352 return GetWrapper()->IsSet();
353 }
354
356 {
357 return GetWrapper()->DelegateSize();
358 }
359
360 private:
361 static constexpr uint32 InlineStorageSize = TotalSize - sizeof(TTaskDelegateBase);
362 mutable char InlineStorage[InlineStorageSize];
363 TTaskDelegateBase CallableWrapper;
364
365 TTaskDelegateBase* GetWrapper()
366 {
367 return UE_LAUNDER(static_cast<TTaskDelegateBase*>(&CallableWrapper));
368 }
369
370 const TTaskDelegateBase* GetWrapper() const
371 {
372 return UE_LAUNDER(static_cast<const TTaskDelegateBase*>(&CallableWrapper));
373 }
374 };
375}
#define checkSlow(expr)
Definition AssertionMacros.h:332
#define checkNoEntry()
Definition AssertionMacros.h:316
#define checkf(expr, format,...)
Definition AssertionMacros.h:315
#define TEXT(x)
Definition Platform.h:1272
AUTORTFM_INFER UE_FORCEINLINE_HINT constexpr auto Invoke(FuncType &&Func, ArgTypes &&... Args) -> decltype(((FuncType &&) Func)((ArgTypes &&) Args...))
Definition Invoke.h:44
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
return true
Definition ExternalRpcRegistry.cpp:601
#define UE_LAUNDER(x)
Definition Launder.h:8
#define ON_SCOPE_EXIT
Definition ScopeExit.h:73
void Move(T &A, typename TMoveSupportTraits< T >::Copy B)
Definition UnrealTemplate.h:24
UE_INTRINSIC_CAST UE_REWRITE constexpr std::remove_reference_t< T > && MoveTemp(T &&Obj) noexcept
Definition UnrealTemplate.h:520
memcpy(InputBufferBase, BinkBlocksData, BinkBlocksSize)
uint32_t uint32
Definition binka_ue_file_header.h:6
TTaskDelegate(TTaskDelegate< ReturnType(ParamTypes...), SourceTotalSize > &&Other)
Definition TaskDelegate.h:276
bool IsHeapAllocated() const
Definition TaskDelegate.h:345
ReturnType CallAndMove(TTaskDelegate< ReturnType(ParamTypes...), DestTotalSize > &Destination, ParamTypes... Params)
Definition TaskDelegate.h:306
TTaskDelegate(const TTaskDelegate< ReturnType(ParamTypes...), SourceTotalSize > &)=delete
ReturnType operator()(ParamTypes... Params) const
Definition TaskDelegate.h:300
TTaskDelegate(CallableT &&Callable)
Definition TaskDelegate.h:282
uint32 DelegateSize() const
Definition TaskDelegate.h:355
Definition TaskDelegate.h:40
Definition ConcurrentLinearAllocator.h:65
Definition ConcurrentLinearAllocator.h:190
static void Free(void *Pointer)
Definition ConcurrentLinearAllocator.h:485
ReturnType MakeDummyValue()
Definition TaskDelegate.h:26
void MakeDummyValue< void >()
Definition TaskDelegate.h:32
Definition Scheduler.cpp:25
FUniformParams Params
Definition MeshPaintVirtualTexture.cpp:162
VERSECOMPILER_API bool IsSet()
Definition CommandLine.cpp:63
@ false
Definition radaudio_common.h:23
Definition ConcurrentLinearAllocator.h:154
static constexpr uint32 BlockSize
Definition TaskDelegate.h:14
static constexpr bool InlineBlockAllocation
Definition TaskDelegate.h:17
static constexpr bool RequiresAccurateSize
Definition TaskDelegate.h:16
static constexpr bool AllowOversizedBlocks
Definition TaskDelegate.h:15
static constexpr const char * TagName
Definition TaskDelegate.h:18
Definition IsInvocable.h:47