UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
SceneUpdateCommandQueue.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2#pragma once
3
4#include "CoreMinimal.h"
6#include "RendererInterface.h"
7
9{
10 Added = 1u << 0,
11 Deleted = 1u << 1,
12 Updated = 1u << 2,
15};
17
26template <typename InSceneInfoType, typename EInDirtyFlagsType, typename EInId>
28{
29private:
31
32 struct FBasePayloadArray;
33 template <typename PayloadType>
34 struct TPayloadArray;
35
36public:
39 using FSceneInfoPersistentId = typename InSceneInfoType::FPersistentId;
40
41 using EId = EInId;
42
43 static constexpr int32 MaxId = int32(EId::MAX);
44 static_assert(MaxId <= 64, "The max update ID must fit in the 64 bits we use to store the mask.");
45
51 {
53
54 template <typename PayloadType>
56 {
57 if (PayloadType::IdBit & PayloadMask)
58 {
59 int32 Index = FPlatformMath::CountBits(PayloadMask& PayloadType::ExclusiveIdMask);
60
61 return PayloadDataSlots[Index];
62 }
63 return INDEX_NONE;
64 }
65
66 template <typename PayloadType>
68 {
69 DirtyFlags |= InDirtyFlags;
70
71 int32 Index = FPlatformMath::CountBits(PayloadMask& PayloadType::ExclusiveIdMask);
72 // previously set, replace pointer
73 if (PayloadType::IdBit& PayloadMask)
74 {
75 PayloadDataSlots[Index] = PayloadOffset;
76 }
77 else
78 {
79 PayloadMask |= PayloadType::IdBit;
80 PayloadDataSlots.Insert(PayloadOffset, Index);
81 }
82 }
83
84 FSceneInfo* GetSceneInfo() const { return SceneInfo; }
85 FSceneInfoPersistentId GetPersistentId() const { return PersistentId; }
86 bool IsDelete() const { return bDeleted; }
87 bool IsAdd() const { return bAdded; }
88
89 // Should only be called for added objects after the ID has been allocated.
91 {
92 check(bAdded);
93 PersistentId = InId;
94 }
95
96 private:
97 FSceneInfo* SceneInfo = nullptr;
98 uint64 PayloadMask = 0ull;
99 FSceneInfoPersistentId PersistentId;
100 EDirtyFlags DirtyFlags = EDirtyFlags::None;
101 bool bDeleted = false;
102 bool bAdded = false;
103 // offsets to the stored data (in bit-order)
106 };
107
109 : PayloadArrays(InPlace)
110 {
111 }
112
113 // Prevent copy constructor to avoid issues with the owned payload arrays
116 : PayloadArrays(MoveTemp(Other.PayloadArrays))
117 , Commands(MoveTemp(Other.Commands))
118 {
119#if DO_CHECK
120 check(Other.RaceGuard == 0);
121#endif
122 Other.CommandSlots.Empty();
123 }
124
126 {
127#if DO_CHECK
128 check(RaceGuard == 0);
129#endif
130 }
131
132 bool IsEmpty() const { return Commands.IsEmpty(); }
133
134 int32 NumCommands() const { return Commands.Num(); }
135
136 bool HasCommand(FSceneInfo* SceneInfo) const
137 {
138 return CommandSlots.Find(SceneInfo) != nullptr;
139 }
140
141 const FUpdateCommand* FindCommand(FSceneInfo* SceneInfo) const
142 {
143 Experimental::FHashElementId Id = CommandSlots.FindId(SceneInfo);
144 if (Id.IsValid())
145 {
146 return &Commands[Id.GetIndex()];
147 }
148 return nullptr;
149 }
150
155 void EnqueueDelete(FSceneInfo* SceneInfo)
156 {
157#if DO_CHECK
158 check(RaceGuard == 0);
159#endif
160
161 int32 CommandSlot = GetOrAddCommandSlot(SceneInfo);
162 FUpdateCommand& Command = Commands[CommandSlot];
163
164 Command.bDeleted = true;
165 // We leave any update data in place, as the alternative is doing a remove-swap and then fixing up all the indexes, which seems not-worth it, instead these updates should be skipped.
166 }
167
171 void EnqueueAdd(FSceneInfo* SceneInfo)
172 {
173#if DO_CHECK
174 check(RaceGuard == 0);
175#endif
176
177 // Add should always be the first command.
178 check(CommandSlots.Find(SceneInfo) == nullptr);
179 // It should not have been added to the scene already
180 int32 CommandSlot = GetOrAddCommandSlot(SceneInfo);
181 FUpdateCommand& Command = Commands[CommandSlot];
182 // It should not even be possible for this to happen
183 check(!Command.bAdded);
184 check(!Command.bDeleted);
185 Command.bAdded = true;
186 }
187
191 template <typename PayloadType>
192 void Enqueue(FSceneInfo* SceneInfo, PayloadType&& Payload)
193 {
194#if DO_CHECK
195 check(RaceGuard == 0);
196#endif
197
198 int32 CommandSlot = GetOrAddCommandSlot(SceneInfo);
199 FUpdateCommand& Command = Commands[CommandSlot];
200
201 if (PayloadArrays[int32(PayloadType::Id)] == nullptr)
202 {
203 PayloadArrays[int32(PayloadType::Id)] = MakeUnique<TPayloadArray<PayloadType>>();
204 }
206 check(Payloads != nullptr);
208
209 // Update existing payload (maybe we want to disallow this?)
211 {
212 check(Payloads->CommandSlots[PrevPayloadOffset] == CommandSlot);
213 Payloads->PayloadData[PrevPayloadOffset] = MoveTemp(Payload);
214 }
215 else // New payload for this command
216 {
217 int32 PayloadOffset = Payloads->CommandSlots.Num();
218 Commands[CommandSlot].template SetOrAddPayloadOffset<PayloadType>(PayloadOffset, Payload.GetDirtyFlags());
219 Payloads->CommandSlots.Emplace(CommandSlot);
220 Payloads->PayloadData.Emplace(MoveTemp(Payload));
221 }
222 }
223
227 template <typename PayloadType>
228 PayloadType* GetPayloadPtr(const FUpdateCommand& Command)
229 {
230 if (PayloadArrays[int32(PayloadType::Id)] == nullptr)
231 {
232 return nullptr;
233 }
234
236 check(Payloads != nullptr);
237 int32 PayloadOffset = Command.template GetPayloadOffset<PayloadType>();
238
239 // Update existing payload (maybe we want to disallow this?)
240 if (PayloadOffset != INDEX_NONE)
241 {
242 // cross check
243 check(Payloads->CommandSlots[PayloadOffset] != INDEX_NONE);
244 check(&Commands[Payloads->CommandSlots[PayloadOffset]] == &Command);
245 return &Payloads->PayloadData[PayloadOffset];
246 }
247 return nullptr;
248 }
249
253 void Reset()
254 {
255#if DO_CHECK
256 check(RaceGuard == 0);
257#endif
258
259 CommandSlots.Empty();
260 Commands.Reset();
261 for (const auto& PayloadArray : PayloadArrays)
262 {
263 if (PayloadArray != nullptr)
264 {
265 PayloadArray->Reset();
266 }
267 }
268 }
269
272 template <typename CallbackFuncType>
274 {
275 for (FUpdateCommand& Command : Commands)
276 {
277 if (IsFilterIncludingCommand(Command, CommandFilter))
278 {
279 CallbackFunc(Command);
280 }
281 }
282 }
283
284 template <typename CallbackFuncType>
289
295 template <typename CallbackFuncType>
297 {
298 for (const FUpdateCommand& Command : Commands)
299 {
300 if (!IsFilterIncludingCommand(Command, CommandFilter))
301 {
302 continue;
303 }
304
305 // Include a command if it is added/deleted OR matches the mask (the former can be excluded in the CommandFilter)
306 if (Command.bAdded || Command.bDeleted || (UpdatePayloadMask& Command.PayloadMask) != 0)
307 {
308 CallbackFunc(Command);
309 }
310 }
311 }
312
318 template <typename CallbackFuncType>
320 {
321 for (const FUpdateCommand& Command : Commands)
322 {
323 if (!IsFilterIncludingCommand(Command, CommandFilter))
324 {
325 continue;
326 }
327
328 if (Command.bAdded || Command.bDeleted || EnumHasAnyFlags(Command.DirtyFlags, DirtyFlags))
329 {
330 CallbackFunc(Command);
331 }
332 }
333 }
334
340 template <typename PayloadType>
342 {
343 struct FItem
344 {
345 const PayloadType& Payload;
347 };
348
356
358 {
359 while (Payloads && Index < Payloads->CommandSlots.Num() && Commands[Payloads->CommandSlots[Index]].bDeleted)
360 {
361 ++Index;
362 }
363 }
364
366 {
367 ++Index;
368 SkipDeleted();
369 }
370
372 {
373 check(Payloads && Index < Payloads->CommandSlots.Num());
374 const FUpdateCommand& Command = Commands[Payloads->CommandSlots[Index]];
375 check(!Command.bDeleted);
376 return { Payloads->PayloadData[Index], Command.SceneInfo };
377 }
378
379 bool operator != (const TConstPayloadIterator& It ) const
380 {
381 check(Payloads == It.Payloads);
382 return Index != It.Index;
383 }
384
385 explicit operator bool() const { return Payloads!= nullptr && Index < Payloads->CommandSlots.Num(); }
386
387 private:
388 const TPayloadArray<PayloadType>* Payloads;
390 int32 Index = 0;
391 };
392
396 template <typename PayloadType>
402
406 template <typename PayloadType>
408 {
410 return Payloads != nullptr ? Payloads->PayloadData.Num() : 0;
411 }
412
413 template <typename PayloadType>
415 {
417 : UpdateBuffer(InUpdateBuffer)
418 , NumItems(InNumItems)
419 {
420#if DO_CHECK
421 UpdateBuffer.BeginReadAccess();
422#endif
423 }
425 {
426#if DO_CHECK
427 UpdateBuffer.EndReadAccess();
428#endif
429 }
430
431 TConstPayloadIterator<PayloadType> begin() const { return UpdateBuffer.GetIterator<PayloadType>(); }
432 TConstPayloadIterator<PayloadType> end() const { return UpdateBuffer.GetEndIterator<PayloadType>(); }
433
434 int32 Num() const { return NumItems; }
435
436 bool IsEmpty() const { return NumItems == 0; }
437
438 private:
439 TSceneUpdateCommandQueue& UpdateBuffer;
440 int32 NumItems = 0;
441 };
442
448 template <typename PayloadType>
453
458 template <EId InId, EDirtyFlags InDirtyFlags>
460 {
461 // unique ID for the type of update, can be made to support registration like the SceneExtensions to rather than be compile time.
462 static constexpr EId Id = InId;
463 static constexpr uint64 IdBit = 1ull << uint32(Id);
464 static constexpr uint64 ExclusiveIdMask = (1ull << uint32(Id)) - 1ull;
466
467 // Allow static polymorphism to implement per-payload runtime variable flags.
469 };
470
471#if DO_CHECK
472 void BeginReadAccess()
473 {
474 ++RaceGuard;
475 }
476
477 void EndReadAccess()
478 {
479 --RaceGuard;
480 }
481
482 struct FReadAccessScope
483 {
485 UpdateQueue(InUpdateQueue)
486 {
487 UpdateQueue.BeginReadAccess();
488 }
490 {
491 UpdateQueue.EndReadAccess();
492 }
493 protected:
494 TSceneUpdateCommandQueue& UpdateQueue;
495 };
496#endif
497
498private:
502 template <typename PayloadType>
503 TConstPayloadIterator<PayloadType> GetEndIterator() const
504 {
506 return TConstPayloadIterator<PayloadType>(Payloads, Commands, Payloads != nullptr ? Payloads->PayloadData.Num() : 0);
507 }
508
509 struct FBasePayloadArray
510 {
511 FBasePayloadArray(int32 InPayloadByteSize) : PayloadByteSize(InPayloadByteSize) {}
512 virtual ~FBasePayloadArray() { };
513 virtual void Reset() = 0;
514
515 int32 PayloadByteSize = 0;
516 };
517
518 template <typename PayloadType>
519 struct TPayloadArray : public FBasePayloadArray
520 {
522 TArray<int32, FAllocator> CommandSlots;
523
524 TPayloadArray() : FBasePayloadArray(sizeof(PayloadType)) {}
525
526 virtual void Reset() override
527 {
528 PayloadData.Reset();
529 CommandSlots.Reset();
530 }
531 };
532
533 int32 GetOrAddCommandSlot(FSceneInfo* SceneInfo)
534 {
535 bool bWasAlreadyInSet = false;
536 Experimental::FHashElementId Id = CommandSlots.FindOrAddId(SceneInfo, bWasAlreadyInSet);
537 int32 CommandSlot = Id.GetIndex();
538 if (!bWasAlreadyInSet)
539 {
540 // Since we only add to this thing the slots should always go at the end.
541 // Note: since we always grow this table it could easily have a much simpler hash table.
542 check(CommandSlot == Commands.Num());
543 Commands.Emplace(SceneInfo, SceneInfo->GetPersistentIndex());
544 }
545 check(Commands.IsValidIndex(CommandSlot));
546 return CommandSlot;
547 }
548
549 bool IsFilterIncludingCommand(const FUpdateCommand& Command, ESceneUpdateCommandFilter CommandFilter) const
550 {
551 if (Command.bDeleted)
552 {
554 }
555
556 if (Command.bAdded)
557 {
559 }
561 }
562
563 template <typename PayloadType>
564 TPayloadArray<PayloadType>* GetPayloadArray()
565 {
566 check(PayloadArrays[int32(PayloadType::Id)] == nullptr || PayloadArrays[int32(PayloadType::Id)]->PayloadByteSize == sizeof(PayloadType));
567 return static_cast<TPayloadArray<PayloadType>*>(PayloadArrays[int32(PayloadType::Id)].Get());
568 }
569 template <typename PayloadType>
570 const TPayloadArray<PayloadType>* GetPayloadArray() const
571 {
572 return const_cast<TSceneUpdateCommandQueue*>(this)->GetPayloadArray<PayloadType>();
573 }
574
575 using FPayloadArrays = TStaticArray<TUniquePtr<FBasePayloadArray>, 64>;
576 FPayloadArrays PayloadArrays = FPayloadArrays(InPlace);
579
580#if DO_CHECK
581 std::atomic<int> RaceGuard = 0;
582#endif
583};
#define check(expr)
Definition AssertionMacros.h:314
TSizedDefaultAllocator< 32 > FDefaultAllocator
Definition ContainerAllocationPolicies.h:831
@ INDEX_NONE
Definition CoreMiscDefines.h:150
@ InPlace
Definition CoreMiscDefines.h:162
FPlatformTypes::int32 int32
A 32-bit signed integer.
Definition Platform.h:1125
FPlatformTypes::uint64 uint64
A 64-bit unsigned integer.
Definition Platform.h:1117
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
constexpr bool EnumHasAnyFlags(Enum Flags, Enum Contains)
Definition EnumClassFlags.h:35
#define ENUM_CLASS_FLAGS(Enum)
Definition EnumClassFlags.h:6
const bool
Definition NetworkReplayStreaming.h:178
ESceneUpdateCommandFilter
Definition SceneUpdateCommandQueue.h:9
UE_INTRINSIC_CAST UE_REWRITE constexpr std::remove_reference_t< T > && MoveTemp(T &&Obj) noexcept
Definition UnrealTemplate.h:520
uint32_t uint32
Definition binka_ue_file_header.h:6
Definition RobinHoodHashTable.h:72
FindValueType Find(const KeyType &Key)
Definition RobinHoodHashTable.h:918
FHashElementId FindId(const KeyType &Key) const
Definition RobinHoodHashTable.h:901
void Empty()
Definition RobinHoodHashTable.h:1059
Definition RobinHoodHashTable.h:1419
Definition Array.h:670
void Reset(SizeType NewSize=0)
Definition Array.h:2246
Definition SceneUpdateCommandQueue.h:28
void Enqueue(FSceneInfo *SceneInfo, PayloadType &&Payload)
Definition SceneUpdateCommandQueue.h:192
void ForEachCommand(CallbackFuncType CallbackFunc)
Definition SceneUpdateCommandQueue.h:285
const FUpdateCommand * FindCommand(FSceneInfo *SceneInfo) const
Definition SceneUpdateCommandQueue.h:141
static constexpr int32 MaxId
Definition SceneUpdateCommandQueue.h:43
int32 GetNumItems() const
Definition SceneUpdateCommandQueue.h:407
typename InSceneInfoType::FPersistentId FSceneInfoPersistentId
Definition SceneUpdateCommandQueue.h:39
TSceneUpdateCommandQueue()
Definition SceneUpdateCommandQueue.h:108
TSceneUpdateCommandQueue(TSceneUpdateCommandQueue &&Other)
Definition SceneUpdateCommandQueue.h:115
void ForEachUpdateCommand(ESceneUpdateCommandFilter CommandFilter, uint64 UpdatePayloadMask, CallbackFuncType CallbackFunc) const
Definition SceneUpdateCommandQueue.h:296
EInDirtyFlagsType EDirtyFlags
Definition SceneUpdateCommandQueue.h:37
bool HasCommand(FSceneInfo *SceneInfo) const
Definition SceneUpdateCommandQueue.h:136
void EnqueueDelete(FSceneInfo *SceneInfo)
Definition SceneUpdateCommandQueue.h:155
void EnqueueAdd(FSceneInfo *SceneInfo)
Definition SceneUpdateCommandQueue.h:171
TSceneUpdateCommandQueue(const TSceneUpdateCommandQueue &)=delete
TConstPayloadIterator< PayloadType > GetIterator() const
Definition SceneUpdateCommandQueue.h:397
bool IsEmpty() const
Definition SceneUpdateCommandQueue.h:132
void Reset()
Definition SceneUpdateCommandQueue.h:253
InSceneInfoType FSceneInfo
Definition SceneUpdateCommandQueue.h:38
TPayloadRangeView< PayloadType > GetRangeView()
Definition SceneUpdateCommandQueue.h:449
~TSceneUpdateCommandQueue()
Definition SceneUpdateCommandQueue.h:125
EInId EId
Definition SceneUpdateCommandQueue.h:41
PayloadType * GetPayloadPtr(const FUpdateCommand &Command)
Definition SceneUpdateCommandQueue.h:228
int32 NumCommands() const
Definition SceneUpdateCommandQueue.h:134
void ForEachCommand(ESceneUpdateCommandFilter CommandFilter, CallbackFuncType CallbackFunc)
Definition SceneUpdateCommandQueue.h:273
void ForEachUpdateCommand(ESceneUpdateCommandFilter CommandFilter, EDirtyFlags DirtyFlags, CallbackFuncType CallbackFunc) const
Definition SceneUpdateCommandQueue.h:319
Definition ContainerAllocationPolicies.h:830
Definition StaticArray.h:26
const Type Command
Definition GenericApplication.h:46
U16 Index
Definition radfft.cpp:71
Definition SceneUpdateCommandQueue.h:51
void SetPersistentId(FSceneInfoPersistentId InId)
Definition SceneUpdateCommandQueue.h:90
bool IsAdd() const
Definition SceneUpdateCommandQueue.h:87
FSceneInfo * GetSceneInfo() const
Definition SceneUpdateCommandQueue.h:84
bool IsDelete() const
Definition SceneUpdateCommandQueue.h:86
FUpdateCommand(FSceneInfo *InSceneInfo, FSceneInfoPersistentId InPersistentId)
Definition SceneUpdateCommandQueue.h:52
int32 GetPayloadOffset() const
Definition SceneUpdateCommandQueue.h:55
void SetOrAddPayloadOffset(int32 PayloadOffset, EDirtyFlags InDirtyFlags)
Definition SceneUpdateCommandQueue.h:67
FSceneInfoPersistentId GetPersistentId() const
Definition SceneUpdateCommandQueue.h:85
Definition SceneUpdateCommandQueue.h:344
FSceneInfo * SceneInfo
Definition SceneUpdateCommandQueue.h:346
const PayloadType & Payload
Definition SceneUpdateCommandQueue.h:345
Definition SceneUpdateCommandQueue.h:342
TConstPayloadIterator(const TPayloadArray< PayloadType > *InPayloads, const TArray< FUpdateCommand, FAllocator > &InCommands, int32 InIndex=0)
Definition SceneUpdateCommandQueue.h:349
void operator++()
Definition SceneUpdateCommandQueue.h:365
bool operator!=(const TConstPayloadIterator &It) const
Definition SceneUpdateCommandQueue.h:379
void SkipDeleted()
Definition SceneUpdateCommandQueue.h:357
FItem operator*() const
Definition SceneUpdateCommandQueue.h:371
Definition SceneUpdateCommandQueue.h:460
static constexpr uint64 IdBit
Definition SceneUpdateCommandQueue.h:463
EDirtyFlags GetDirtyFlags() const
Definition SceneUpdateCommandQueue.h:468
static constexpr EId Id
Definition SceneUpdateCommandQueue.h:462
static constexpr uint64 ExclusiveIdMask
Definition SceneUpdateCommandQueue.h:464
static constexpr EDirtyFlags DirtyFlags
Definition SceneUpdateCommandQueue.h:465
Definition SceneUpdateCommandQueue.h:415
int32 Num() const
Definition SceneUpdateCommandQueue.h:434
TConstPayloadIterator< PayloadType > end() const
Definition SceneUpdateCommandQueue.h:432
TPayloadRangeView(TSceneUpdateCommandQueue &InUpdateBuffer, int32 InNumItems)
Definition SceneUpdateCommandQueue.h:416
~TPayloadRangeView()
Definition SceneUpdateCommandQueue.h:424
bool IsEmpty() const
Definition SceneUpdateCommandQueue.h:436
TConstPayloadIterator< PayloadType > begin() const
Definition SceneUpdateCommandQueue.h:431