UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
InstanceAttributeTracker.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#include "CoreTypes.h"
6#include "HAL/UnrealMemory.h"
9#include "Containers/Array.h"
10#include "InstanceDataTypes.h"
11
12// Implementation details
14{
15template <typename ElementType>
17{
18 // Utility to, at compile time, replicate the given bit mask 'Mask' as many times as MaskSize inside sizeof(ElementType)*8.
19 // e.g. turning Mask=0x1 into 0x11111111 if MaskSize=4 & ElementType is uint32
20 template <ElementType Mask, uint32 MaskSize>
21 struct FRepMask
22 {
23 template <uint32 Step>
24 struct FIterator;
25 template <>
26 struct FIterator<0u>
27 {
28 static constexpr ElementType Result = Mask;
29 };
30 template <uint32 Step>
31 struct FIterator
32 {
33 static constexpr ElementType Result = (Mask << (Step * MaskSize)) | FIterator<Step - 1u>::Result;
34
35 };
36
37 static constexpr uint32 NumSteps = (sizeof(ElementType) * 8u / MaskSize) - 1u;
38 static constexpr ElementType Result = FIterator<NumSteps>::Result;
39 };
43 static inline uint32 CountTrailingZeros(ElementType Value);
44};
45
46
47template <>
49{
50 return FMath::CountTrailingZeros(Value);
51}
52template <>
54{
55 return uint32(FMath::CountTrailingZeros64(Value));
56}
57
58
59} // namespace UE::InstanceAttributeTracker::Implementation
60
67{
68public:
69 // Underlying type to store per-instance masks packed in. For example, with EFlag::Num == 4, and uint32 we can store 8 masks in each element.
71
73
78 enum class EFlag : ElementType
79 {
80 Added,
84 Num,
85 };
86
87 static constexpr uint32 BitsPerElement = sizeof(ElementType) * 8u;
89 static constexpr ElementType AnyFlagMask = (ElementType(1U) << uint32(EFlag::Num)) - ElementType(1U);
90
91 // Helper struct to convert the flag to its bitmask at comile time.
92 template <EFlag Flag>
93 struct FToBit
94 {
95 static constexpr ElementType Bit = ElementType(1U) << uint32(Flag);
96 };
97
101
105
109
112 void Reset();
113
116 static void Move(FInstanceAttributeTracker &Dest, FInstanceAttributeTracker &Source);
117
122
128 {
129 FirstRemovedIdIndex = FMath::Min(FirstRemovedIdIndex, Id.GetAsIndex());
130 // Lazy allocate the memory so there is nothing to move around in case nothing is removed
131 if (Id.GetAsIndex() >= Removed.Num())
132 {
133 // TBitArray does a lot of work for each added bit, first clearing the slack redundantly and then also clearing the bit even though it was already cleared because the slack was cleared...
134 // so we pad the allocation request with at least a dword at a time.
135 int32 RemovedPadded = FMath::DivideAndRoundUp(Id.GetAsIndex() + 1, 32) * 32;
136 Removed.SetNum(RemovedPadded, false);
137 }
138 Removed[Id.GetAsIndex()] = true;
139 }
140
141
146 {
147 Validate();
148
149 SetNum(FMath::Max(Index, MaxInstanceIndex));
150
152
153 // book-keeping
154 DecChangedCounts(Index);
155
156 // Remove the tracked state bits
157 int32 LastIndex = MaxIndex - 1;
158 if (Index != LastIndex)
159 {
160 // Move tracked & tag as moved.
161 ElementType PrevFlags = GetFlags(LastIndex);
163 {
164 check(NumChanged[uint32(EFlag::IndexChanged)] >= 0);
165 NumChanged[uint32(EFlag::IndexChanged)] += 1;
166 }
167 SetMaskTo(Index, FToBit<EFlag::IndexChanged>::Bit | PrevFlags);
168 FirstChangedIndex = FMath::Min(FirstChangedIndex, Index);
169 LastChangedIndex = FMath::Max(LastChangedIndex, Index);
170 }
171 // clear the last one so that it is all zero after the last mask (important for when we do implicit add, as we do at the moment).
172 ClearLastElement();
173 MaxIndex -= 1;
174
175 Validate();
176 }
177
183 {
184 Validate();
185
186 SetNum(FMath::Max(Index, MaxInstanceIndex));
187
189
190 DecChangedCounts(Index);
191
192 // Remove the tracked state bits
193 int32 LastIndex = MaxIndex - 1;
194 if (Index != LastIndex)
195 {
197 int32 LastElementIndex = LastIndex / MasksPerElement;
198
199 ElementType CurrentElement = Data[FirstElementIndex];
200 ElementType NextElement = LoadCountAndMask(FirstElementIndex + 1, FirstElementIndex != LastElementIndex);
201 // 1. Move up to "Num" bits to cover the removed item, shifting in zero.
202 {
204 // Count mask changes only in the moved part (not including the removed or any previous).
206 CountMaskChanges(CurrentElement, ElementSubIndex + 1, NumMasksInElement);
207 ElementType CurrentElementResult = (CurrentElement >> uint32(EFlag::Num)) | IndexChangedElementMask;
208 if (ElementSubIndex != 0u)
209 {
212 // we shifted some as we shouldn't, put them back
213 CurrentElementResult = (CurrentElementResult & Mask) | (CurrentElement & (~Mask));
214 }
215 // Move the first item from the next step
216 CurrentElementResult |= NextElement << (MasksPerElement - 1u) * uint32(EFlag::Num);
218 }
219 // 2. Do the middle, whole, elements...
220 for (int32 ElementIndex = FirstElementIndex + 1; ElementIndex < LastElementIndex; ++ElementIndex)
221 {
222 CurrentElement = NextElement >> uint32(EFlag::Num);
223 NextElement = LoadCountAndMask(ElementIndex + 1, true);
224 ElementType CurrentElementResult = CurrentElement | NextElement << (MasksPerElement - 1u) * uint32(EFlag::Num);
225 Data[ElementIndex] = CurrentElementResult;
226 }
227 // 2. Do the last element...
228 if (LastElementIndex != FirstElementIndex)
229 {
230 Data[LastElementIndex] = NextElement >> uint32(EFlag::Num);
231 }
232
233 FirstChangedIndex = FMath::Min(FirstChangedIndex, Index);
234 LastChangedIndex = FMath::Max(LastChangedIndex, LastIndex);
235 }
236 ClearLastElement();
237 MaxIndex -= 1;
238
239 Validate();
240 }
241
244 template <EFlag Flag>
246 {
247 Validate();
248
249 // Lazy allocate
250 SetNum(FMath::Max(Index, MaxInstanceIndex));
251
252 check(Index < MaxIndex);
253
254 FirstChangedIndex = FMath::Min(FirstChangedIndex, Index);
255 LastChangedIndex = FMath::Max(LastChangedIndex, Index);
256
258
260
261 int32 ElementIndex = Index / MasksPerElement;
262
263 ElementType PrevElement = Data[ElementIndex];
264
266 {
267 check(NumChanged[uint32(Flag)] >= 0);
268 NumChanged[uint32(Flag)] += 1;
269 }
270
271 Data[ElementIndex] = PrevElement | SetMask;
272
273 Validate();
274 }
275
280 {
281 check(Index < MaxIndex);
282 ElementType Element = Data[Index / MasksPerElement];
284 return (Element >> ElementSubShift) & AnyFlagMask;
285 }
286
290 template <EFlag Flag>
291 inline bool TestFlag(int32 Index) const
292 {
293 return (GetFlags(Index) & FToBit<Flag>::Bit) != 0u;
294 }
295
299 {
300 Validate();
301
303
304 if (ElementIndex > Data.Num())
305 {
306 Data.SetNumZeroed(ElementIndex);
307 }
308 MaxIndex = InstanceIndexMax;
309 Validate();
310 }
311
315 template <uint32 Mask = AnyFlagMask>
317 {
318 public:
319 inline FAnyValidIterator() = default;
321 {
322 ElementIndex = StartIndex / MasksPerElement;
323 ElementOffset = StartIndex % MasksPerElement;
324 CurrentElement = Tracker->Data.IsValidIndex(ElementIndex) ? (Tracker->Data[ElementIndex] & ElementMask) : 0;
325 CurrentElement >>= uint32(EFlag::Num) * ElementOffset;
327 }
328
329 inline void AdvanceUntilAny()
330 {
331 while(ElementIndex < Tracker->Data.Num())
332 {
333 // Skip the whole word if nothing is set.
334 if (CurrentElement)
335 {
338 CurrentElement >>= uint32(EFlag::Num) * Steps;
339 ElementOffset += Steps;
340 return;
341 }
342 ++ElementIndex;
343 ElementOffset = 0;
344 if (ElementIndex >= Tracker->Data.Num())
345 {
346 CurrentElement = 0u;
347 return;
348 }
349 CurrentElement = Tracker->Data[ElementIndex] & ElementMask;
350 }
351 }
352
353 inline void AdvanceToNext()
354 {
355 // Clear the current value (which will cause AdvanceUntilAny to move forwards)
356 CurrentElement &= ~ElementType(Mask);
358 }
359
361 {
363 return *this;
364 }
365
366 inline explicit operator bool() const
367 {
368 return GetIndex() < FMath::Min(Tracker->MaxIndex, Tracker->LastChangedIndex + 1);
369 }
370
371 inline int32 GetIndex() const
372 {
373 return ElementIndex * MasksPerElement + ElementOffset;
374 }
375
376 /*
377 * Returns the mask for the current item
378 * NOTE: does not clear any bits outside the search mask.
379 */
380 inline uint32 GetMask() const
381 {
382 return CurrentElement;
383 }
384
385 template <EFlag Flag>
386 inline bool TestFlag() const
387 {
388 return (CurrentElement & FToBit<Flag>::Bit) != 0u;
389 }
390 private:
391 const FInstanceAttributeTracker *Tracker = nullptr;
392 int32 ElementIndex = 0;
393 ElementType CurrentElement;
394 int32 ElementOffset = 0;
395 static constexpr ElementType ElementMask = FBitManip::FRepMask<Mask, uint32(EFlag::Num)>::Result;
396 };
397
398 template <uint32 Mask = AnyFlagMask>
400 {
401 return FAnyValidIterator<Mask>(this, FMath::Min(MaxIndex, FirstChangedIndex));
402 }
403
404#if DO_GUARD_SLOW
405 ENGINE_API void Validate() const;
406#else
407 inline void Validate() const { }
408#endif
409
414 template <EFlag Flag>
416 {
417 public:
419
420 inline FDeltaRange() = default;
421
423 : bFullUpdate(bInFullUpdate)
424 , NumItems(InNumItems)
425 , Tracker(InInstanceUpdateTracker)
426 {
427 int32 NumAdded = Tracker->NumChanged[uint32(EFlag::Added)];
428 bFullUpdate = bFullUpdate || (NumAdded + Tracker->NumChanged[uint32(Flag)] == NumItems);
429 if (!bFullUpdate)
430 {
431 NumItems = NumAdded + Tracker->NumChanged[uint32(Flag)];
432 }
433 }
434
437 inline bool IsEmpty() const { return NumItems == 0; }
438
441 inline bool IsDelta() const { return !bFullUpdate; }
442
446 inline int32 GetNumItems() const { return NumItems; }
447
454 {
455 bool bUseIterator = true;
459
466
474
475 inline void operator++()
476 {
477 ++ItemIndex;
478 if (bUseIterator)
479 {
480 ++It;
481 }
482 }
483
487 inline int32 GetIndex() const
488 {
489 if (bUseIterator)
490 {
491 return It.GetIndex();
492 }
493 return ItemIndex;
494 }
495
499 inline int32 GetItemIndex() const
500 {
501 return ItemIndex;
502 }
503
504 inline explicit operator bool() const
505 {
506 if (bUseIterator)
507 {
508 return bool(It);
509 }
510 return ItemIndex < MaxNum;
511 }
512 };
513
515 {
516 if (bFullUpdate)
517 {
518 return FConstIterator(0, NumItems);
519 }
521 }
522
523 private:
524 bool bFullUpdate = true;
525 int32 NumItems = 0;
526 const FInstanceAttributeTracker *Tracker = nullptr;
527 };
528
529 template <EFlag Flag>
535
536 inline bool HasAnyChanges() const
537 {
538 return MaxIndex > 0 || !Removed.IsEmpty();
539 }
540
542 {
543 return Data.GetAllocatedSize() + Removed.GetAllocatedSize();
544 }
545
546private:
547
550 inline void SetMaskTo(int32 Index, ElementType Mask)
551 {
552 check(Index < MaxIndex);
554
555 const int32 ElementIndex = Index / MasksPerElement;
557 // Load and clear the mask for this index
558 ElementType CurrentElement = Data[ElementIndex] & ~(AnyFlagMask << ElementSubShift);
559 // add the new mask and store
560 Data[ElementIndex] = CurrentElement | (Mask << ElementSubShift);
561 }
562
566 inline void ClearLastElement()
567 {
568 int32 LastIndex = MaxIndex - 1;
569 const int32 ElementIndex = LastIndex / MasksPerElement;
570 const int32 SubIndex = LastIndex % MasksPerElement;
571 if (SubIndex > 0)
572 {
574 // Load and clear the mask for this index
575 ElementType CurrentElement = Data[ElementIndex] & ((~ElementType(0)) >> ElementSubShift);
576 Data[ElementIndex] = CurrentElement;
577 }
578 else
579 {
580 Data[ElementIndex] = ElementType(0);
581 }
582 }
583
586 template <EFlag Flag>
587 inline void DecChangedCount(uint32 Flags)
588 {
590 {
591 NumChanged[uint32(Flag)] -= 1;
592 check(NumChanged[uint32(Flag)] >= 0);
593 }
594 }
595
598 inline void DecChangedCounts(int32 Index)
599 {
600 const uint32 PrevFlags = GetFlags(Index);
601 if ((PrevFlags & FToBit<EFlag::Added>::Bit) == 0)
602 {
606 }
607 else
608 {
609 NumChanged[uint32(EFlag::Added)] -= 1;
610 check(NumChanged[uint32(EFlag::Added)] >= 0);
611 }
612 }
613
616 inline void CountMaskChanges(ElementType Element, uint32 Offset, uint32 NumMasksInElement)
617 {
618 // If the offset is equal to the number of masks then there can't be any changes
620 {
621 // 1. Element & IndexChangedElementMask - 1 if it already had index changed
622 ElementType HasIndexChanged = (Element & IndexChangedElementMask);
623 // 2. Element & AddedElementMask - 1 if already had added << to align
626
627 int32 Delta = NumMasksInElement - Offset - FMath::CountBits(CombinedMask >> (Offset * uint32(EFlag::Num)));
628 check(Delta >= 0);
629 NumChanged[uint32(EFlag::IndexChanged)] += Delta;
630 }
631 };
635 inline ElementType LoadCountAndMask(int32 ElementIndex, bool bHasNextElement)
636 {
637 if (bHasNextElement)
638 {
639 uint32 NumMasksInElement = FMath::Min(MasksPerElement, MaxIndex - ElementIndex * MasksPerElement);
640 ElementType Element = Data[ElementIndex];
641 CountMaskChanges(Element, 0, NumMasksInElement);
642 return Element | IndexChangedElementMask;
643 }
644 return ElementType(0);
645 };
646
647
648private:
651
653 TBitArray<> Removed;
654
656 int32 MaxIndex = 0;
657
659 int32 FirstRemovedIdIndex = MAX_int32;
660
662 int32 FirstChangedIndex = MAX_int32;
663 int32 LastChangedIndex = 0;
664
670 int32 NumChanged[uint32(EFlag::Num)];
671
673 static constexpr ElementType IndexChangedElementMask = FBitManip::FRepMask<FToBit<EFlag::IndexChanged>::Bit, uint32(EFlag::Num)>::Result;
674 static constexpr ElementType AddedElementMask = FBitManip::FRepMask<FToBit<EFlag::Added>::Bit, uint32(EFlag::Num)>::Result;
675};
#define check(expr)
Definition AssertionMacros.h:314
FPlatformTypes::SIZE_T SIZE_T
An unsigned integer the same size as a pointer, the same as UPTRINT.
Definition Platform.h:1150
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
return true
Definition ExternalRpcRegistry.cpp:601
const bool
Definition NetworkReplayStreaming.h:178
#define MAX_int32
Definition NumericLimits.h:25
UE_INTRINSIC_CAST UE_REWRITE constexpr std::remove_reference_t< T > && MoveTemp(T &&Obj) noexcept
Definition UnrealTemplate.h:520
uint32 Offset
Definition VulkanMemory.cpp:4033
uint32_t uint32
Definition binka_ue_file_header.h:6
Definition InstanceAttributeTracker.h:317
uint32 GetMask() const
Definition InstanceAttributeTracker.h:380
FAnyValidIterator & operator++()
Definition InstanceAttributeTracker.h:360
void AdvanceUntilAny()
Definition InstanceAttributeTracker.h:329
int32 GetIndex() const
Definition InstanceAttributeTracker.h:371
void AdvanceToNext()
Definition InstanceAttributeTracker.h:353
bool TestFlag() const
Definition InstanceAttributeTracker.h:386
FAnyValidIterator(const FInstanceAttributeTracker *InTracker, int32 StartIndex)
Definition InstanceAttributeTracker.h:320
Definition InstanceAttributeTracker.h:416
static constexpr uint32 IteratorMask
Definition InstanceAttributeTracker.h:418
FConstIterator GetIterator() const
Definition InstanceAttributeTracker.h:514
FDeltaRange(bool bInFullUpdate, int32 InNumItems, const FInstanceAttributeTracker *InInstanceUpdateTracker)
Definition InstanceAttributeTracker.h:422
int32 GetNumItems() const
Definition InstanceAttributeTracker.h:446
bool IsEmpty() const
Definition InstanceAttributeTracker.h:437
bool IsDelta() const
Definition InstanceAttributeTracker.h:441
Definition InstanceAttributeTracker.h:67
static constexpr ElementType AnyFlagMask
Definition InstanceAttributeTracker.h:89
void RemoveAt(FPrimitiveInstanceId Id, int32 Index, int32 MaxInstanceIndex)
Definition InstanceAttributeTracker.h:182
bool HasAnyChanges() const
Definition InstanceAttributeTracker.h:536
void RemoveAtSwap(FPrimitiveInstanceId Id, int32 Index, int32 MaxInstanceIndex)
Definition InstanceAttributeTracker.h:145
bool TestFlag(int32 Index) const
Definition InstanceAttributeTracker.h:291
ElementType GetFlags(int32 Index) const
Definition InstanceAttributeTracker.h:279
static constexpr int32 MasksPerElement
Definition InstanceAttributeTracker.h:88
static constexpr uint32 BitsPerElement
Definition InstanceAttributeTracker.h:87
void Validate() const
Definition InstanceAttributeTracker.h:407
uint32 ElementType
Definition InstanceAttributeTracker.h:70
void SetNum(int32 InstanceIndexMax)
Definition InstanceAttributeTracker.h:298
void MarkIndex(int32 Index, int32 MaxInstanceIndex)
Definition InstanceAttributeTracker.h:245
FAnyValidIterator< Mask > GetChangedIterator() const
Definition InstanceAttributeTracker.h:399
EFlag
Definition InstanceAttributeTracker.h:79
FDeltaRange< Flag > GetDeltaRange(bool bForceFullUpdate, int32 InNumItems) const
Definition InstanceAttributeTracker.h:530
SIZE_T GetAllocatedSize() const
Definition InstanceAttributeTracker.h:541
void operator=(FInstanceAttributeTracker &&Other)
Definition InstanceAttributeTracker.cpp:15
void Reset()
Definition InstanceAttributeTracker.cpp:20
void MarkRemoved(FPrimitiveInstanceId Id)
Definition InstanceAttributeTracker.h:127
TConstSetBitIterator GetRemovedIterator() const
Definition InstanceAttributeTracker.cpp:44
static void Move(FInstanceAttributeTracker &Dest, FInstanceAttributeTracker &Source)
Definition InstanceAttributeTracker.cpp:31
FInstanceAttributeTracker()
Definition InstanceAttributeTracker.cpp:5
Definition Array.h:670
UE_REWRITE SizeType Num() const
Definition Array.h:1144
void SetNumZeroed(SizeType NewNum, EAllowShrinking AllowShrinking=UE::Core::Private::AllowShrinkingByDefault< AllocatorType >())
Definition Array.h:2340
UE_NODEBUG UE_FORCEINLINE_HINT bool IsValidIndex(SizeType Index) const
Definition Array.h:1122
UE_NODEBUG UE_FORCEINLINE_HINT SIZE_T GetAllocatedSize(void) const
Definition Array.h:1059
UE_FORCEINLINE_HINT int32 Num() const
Definition BitArray.h:1466
uint32 GetAllocatedSize(void) const
Definition BitArray.h:1062
FORCENOINLINE void SetNum(int32 InNumBits, ValueType bValue)
Definition BitArray.h:870
bool IsEmpty() const
Definition BitArray.h:1461
Definition BitArray.h:1944
@ Element
Definition Visu.h:18
Definition InstanceAttributeTracker.h:14
@ false
Definition radaudio_common.h:23
U16 Index
Definition radfft.cpp:71
Definition InstanceAttributeTracker.h:454
FAnyValidIterator< IteratorMask > It
Definition InstanceAttributeTracker.h:458
int32 ItemIndex
Definition InstanceAttributeTracker.h:456
FConstIterator(int32 InIndex, int32 InMaxNum)
Definition InstanceAttributeTracker.h:460
int32 GetItemIndex() const
Definition InstanceAttributeTracker.h:499
int32 GetIndex() const
Definition InstanceAttributeTracker.h:487
void operator++()
Definition InstanceAttributeTracker.h:475
FConstIterator(FAnyValidIterator< IteratorMask > &&InIt)
Definition InstanceAttributeTracker.h:467
bool bUseIterator
Definition InstanceAttributeTracker.h:455
int32 MaxNum
Definition InstanceAttributeTracker.h:457
Definition InstanceAttributeTracker.h:94
static constexpr ElementType Bit
Definition InstanceAttributeTracker.h:95
static constexpr UE_FORCEINLINE_HINT T DivideAndRoundUp(T Dividend, T Divisor)
Definition UnrealMathUtility.h:694
Definition InstanceDataTypes.h:19
static constexpr ElementType Result
Definition InstanceAttributeTracker.h:33
static constexpr uint32 NumSteps
Definition InstanceAttributeTracker.h:37
static constexpr ElementType Result
Definition InstanceAttributeTracker.h:38
Definition InstanceAttributeTracker.h:17
static uint32 CountTrailingZeros(ElementType Value)