Files
UnrealEngine/Engine/Source/Runtime/Renderer/Private/VT/UniqueRequestList.h
2025-05-18 13:04:45 +08:00

503 lines
20 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Containers/HashTable.h"
#include "VirtualTexturing.h"
#include "VirtualTextureSystem.h"
#include "VirtualTextureProducer.h"
#include "VirtualTexturePhysicalSpace.h"
union FMappingRequest
{
inline FMappingRequest() {}
inline FMappingRequest(uint16 InLoadIndex, uint8 InPhysicalGroupIndex, uint8 InSpaceID, uint8 InPageTableLayerIndex, uint32 InMaxLevel, uint32 InAddress, uint8 InLevel, uint8 InLocalLevel)
: vAddress(InAddress), vLevel(InLevel), SpaceID(InSpaceID), LoadRequestIndex(InLoadIndex), Local_vLevel(InLocalLevel), ProducerPhysicalGroupIndex(InPhysicalGroupIndex), PageTableLayerIndex(InPageTableLayerIndex), MaxLevel(InMaxLevel)
{}
uint64 PackedValue;
struct
{
uint32 vAddress : 24;
uint32 vLevel : 4;
uint32 SpaceID : 4;
uint32 LoadRequestIndex : 16;
uint32 Local_vLevel : 4;
uint32 ProducerPhysicalGroupIndex : 4;
uint32 PageTableLayerIndex : 4;
uint32 MaxLevel : 4;
};
};
static_assert(sizeof(FMappingRequest) == sizeof(uint64), "Bad packing");
inline bool operator==(const FMappingRequest& Lhs, const FMappingRequest& Rhs) { return Lhs.PackedValue == Rhs.PackedValue; }
inline bool operator!=(const FMappingRequest& Lhs, const FMappingRequest& Rhs) { return Lhs.PackedValue != Rhs.PackedValue; }
union FDirectMappingRequest
{
inline FDirectMappingRequest() {}
inline FDirectMappingRequest(uint8 InSpaceID, uint16 InPhysicalSpaceID, uint8 InPageTableLayerIndex, uint32 InMaxLevel, uint32 InAddress, uint8 InLevel, uint8 InLocalLevel, uint16 InPhysicalAddress)
: vAddress(InAddress), vLevel(InLevel), SpaceID(InSpaceID), pAddress(InPhysicalAddress), PhysicalSpaceID(InPhysicalSpaceID), Local_vLevel(InLocalLevel), MaxLevel(InMaxLevel), PageTableLayerIndex(InPageTableLayerIndex), Pad(0u)
{}
uint32 PackedValue[3];
struct
{
uint32 vAddress : 24;
uint32 vLevel : 4;
uint32 SpaceID : 4;
uint32 pAddress : 16;
uint32 PhysicalSpaceID : 8;
uint32 Local_vLevel : 4;
uint32 MaxLevel : 4;
uint32 PageTableLayerIndex : 4;
uint32 Pad : 28;
};
};
static_assert(sizeof(FDirectMappingRequest) == sizeof(uint32) * 3, "Bad packing");
inline bool operator==(const FDirectMappingRequest& Lhs, const FDirectMappingRequest& Rhs) { return Lhs.PackedValue[0] == Rhs.PackedValue[0] && Lhs.PackedValue[1] == Rhs.PackedValue[1] && Lhs.PackedValue[2] == Rhs.PackedValue[2]; }
inline bool operator!=(const FDirectMappingRequest& Lhs, const FDirectMappingRequest& Rhs) { return !operator==(Lhs, Rhs); }
class FUniqueRequestList
{
public:
explicit FUniqueRequestList(FConcurrentLinearBulkObjectAllocator& Allocator)
: LoadRequestHash(NoInit)
, MappingRequestHash(NoInit)
, DirectMappingRequestHash(NoInit)
, LoadRequests(Allocator.CreateArray<FVirtualTextureLocalTileRequest>(LoadRequestCapacity))
, MappingRequests(Allocator.CreateArray<FMappingRequest>(MappingRequestCapacity))
, DirectMappingRequests(Allocator.CreateArray<FDirectMappingRequest>(DirectMappingRequestCapacity))
, ContinuousUpdateRequests(Allocator.CreateArray<FVirtualTextureLocalTileRequest>(LoadRequestCapacity))
, AdaptiveAllocationsRequests(Allocator.MallocArray<uint32>(LoadRequestCapacity))
, LoadRequestCount(Allocator.MallocArray<uint16>(LoadRequestCapacity))
, LoadRequestGroupMask(Allocator.MallocArray<uint8>(LoadRequestCapacity))
, LoadRequestFlags(Allocator.MallocArray<FLoadRequestFlags>(LoadRequestCapacity))
, NumLoadRequests(0u)
, NumLockRequests(0u)
, NumNonStreamingLoadRequests(0u)
, NumMappingRequests(0u)
, NumDirectMappingRequests(0u)
, NumContinuousUpdateRequests(0u)
, NumAdaptiveAllocationRequests(0u)
{
}
inline void Initialize()
{
LoadRequestHash.Clear();
MappingRequestHash.Clear();
DirectMappingRequestHash.Clear();
ContinuousUpdateRequestHash.Clear();
}
inline void Reset(bool bResetContinousUpdates)
{
LoadRequestHash.Clear();
MappingRequestHash.Clear();
DirectMappingRequestHash.Clear();
NumLoadRequests = 0;
NumLockRequests = 0;
NumNonStreamingLoadRequests = 0;
NumMappingRequests = 0;
NumDirectMappingRequests = 0;
NumAdaptiveAllocationRequests = 0;
if (bResetContinousUpdates)
{
NumContinuousUpdateRequests = 0;
ContinuousUpdateRequestHash.Clear();
}
}
inline uint32 GetNumLoadRequests() const { return NumLoadRequests; }
inline uint32 GetNumNonStreamingLoadRequests() const { return NumNonStreamingLoadRequests; }
inline uint32 GetNumMappingRequests() const { return NumMappingRequests; }
inline uint32 GetNumDirectMappingRequests() const { return NumDirectMappingRequests; }
inline uint32 GetNumContinuousUpdateRequests() const { return NumContinuousUpdateRequests; }
inline uint32 GetNumAdaptiveAllocationRequests() const { return NumAdaptiveAllocationRequests; }
inline const FVirtualTextureLocalTileRequest& GetLoadRequest(uint32 i) const { checkSlow(i < NumLoadRequests); return LoadRequests[i]; }
inline const FMappingRequest& GetMappingRequest(uint32 i) const { checkSlow(i < NumMappingRequests); return MappingRequests[i]; }
inline const FDirectMappingRequest& GetDirectMappingRequest(uint32 i) const { checkSlow(i < NumDirectMappingRequests); return DirectMappingRequests[i]; }
inline const FVirtualTextureLocalTileRequest& GetContinuousUpdateRequest(uint32 i) const { checkSlow(i < NumContinuousUpdateRequests); return ContinuousUpdateRequests[i]; }
inline const uint32& GetAdaptiveAllocationRequest(uint32 i) const { checkSlow(i < NumAdaptiveAllocationRequests); return AdaptiveAllocationsRequests[i]; }
inline uint8 GetGroupMask(uint32 i) const { checkSlow(i < NumLoadRequests); return LoadRequestGroupMask[i]; }
inline bool IsLocked(uint32 i) const { checkSlow(i < NumLoadRequests); return i < NumLockRequests; }
uint16 AddLoadRequest(const FVirtualTextureLocalTileRequest& TileRequest, uint8 GroupMask, uint16 Count, bool bStreamingRequest);
uint16 LockLoadRequest(const FVirtualTextureLocalTileRequest& TileRequest, uint8 GroupMask, bool bStreamingRequest);
void AddMappingRequest(uint16 LoadRequestIndex, uint8 ProducerPhysicalGroupIndex, uint8 SpaceID, uint8 PageTableLayerIndex, uint32 MaxLevel, uint32 vAddress, uint8 vLevel, uint8 Local_vLevel);
void AddDirectMappingRequest(uint8 InSpaceID, uint16 InPhysicalSpaceID, uint8 InPageTableLayerIndex, uint32 MaxLevel, uint32 InAddress, uint8 InLevel, uint8 InLocalLevel, uint16 InPhysicalAddress);
void AddDirectMappingRequest(const FDirectMappingRequest& Request);
void AddContinuousUpdateRequest(const FVirtualTextureLocalTileRequest& Request);
void AddAdaptiveAllocationRequest(uint32 Request);
void MergeRequests(const FUniqueRequestList* RESTRICT Other, FConcurrentLinearBulkObjectAllocator& Allocator);
void SortRequests(FVirtualTextureProducerCollection& Producers, FConcurrentLinearBulkObjectAllocator& Allocator, uint32 MaxNonStreamingLoadRequests, uint32 MaxStreamingLoadRequests, bool bUseCombinedLimit, bool bSortByPriority);
private:
static const uint32 LoadRequestCapacity = 4u * 1024;
static const uint32 MappingRequestCapacity = 8u * 1024 - 256u;
static const uint32 DirectMappingRequestCapacity = MappingRequestCapacity;
static const uint32 ContinuousUpdateRequestCapacity = LoadRequestCapacity;
static const uint32 AdaptiveAllocationRequestCapacity = LoadRequestCapacity;
TStaticHashTable<1024u, LoadRequestCapacity> LoadRequestHash;
TStaticHashTable<1024u, MappingRequestCapacity> MappingRequestHash;
TStaticHashTable<512u, DirectMappingRequestCapacity> DirectMappingRequestHash;
TStaticHashTable<1024u, ContinuousUpdateRequestCapacity> ContinuousUpdateRequestHash;
FVirtualTextureLocalTileRequest* LoadRequests;
FMappingRequest* MappingRequests;
FDirectMappingRequest* DirectMappingRequests;
FVirtualTextureLocalTileRequest* ContinuousUpdateRequests;
uint32* AdaptiveAllocationsRequests;
uint16* LoadRequestCount;
uint8* LoadRequestGroupMask;
struct FLoadRequestFlags
{
uint8 bLocked : 1;
uint8 bStreaming : 1;
uint8 Padding : 6;
FLoadRequestFlags(bool bInLocked, bool bInStreaming)
: bLocked(bInLocked)
, bStreaming(bInStreaming)
{}
};
FLoadRequestFlags* LoadRequestFlags;
uint32 NumLoadRequests;
uint32 NumLockRequests;
uint32 NumNonStreamingLoadRequests;
uint32 NumMappingRequests;
uint32 NumDirectMappingRequests;
uint32 NumContinuousUpdateRequests;
uint32 NumAdaptiveAllocationRequests;
};
uint16 FUniqueRequestList::AddLoadRequest(const FVirtualTextureLocalTileRequest& TileRequest, uint8 GroupMask, uint16 Count, bool bStreamingRequest)
{
const uint16 Hash = TileRequest.GetMurmurHash();
check(GroupMask != 0u);
for (uint16 Index = LoadRequestHash.First(Hash); LoadRequestHash.IsValid(Index); Index = LoadRequestHash.Next(Index))
{
if (TileRequest == LoadRequests[Index])
{
LoadRequests[Index].MergeWith(TileRequest);
check(LoadRequestFlags[Index].bStreaming == bStreamingRequest);
LoadRequestCount[Index] = FMath::Min<uint32>((uint32)LoadRequestCount[Index] + Count, MAX_uint16);
LoadRequestGroupMask[Index] |= GroupMask;
return Index;
}
}
if (NumLoadRequests < LoadRequestCapacity)
{
const uint32 Index = NumLoadRequests++;
LoadRequestHash.Add(Hash, Index);
LoadRequests[Index] = TileRequest;
LoadRequestCount[Index] = Count;
LoadRequestGroupMask[Index] = GroupMask;
LoadRequestFlags[Index] = FLoadRequestFlags(/*bInLocked = */false, bStreamingRequest);
return Index;
}
return 0xffff;
}
uint16 FUniqueRequestList::LockLoadRequest(const FVirtualTextureLocalTileRequest& TileRequest, uint8 GroupMask, bool bStreamingRequest)
{
const uint16 Hash = TileRequest.GetMurmurHash();
check(GroupMask != 0u);
for (uint16 Index = LoadRequestHash.First(Hash); LoadRequestHash.IsValid(Index); Index = LoadRequestHash.Next(Index))
{
if (TileRequest == LoadRequests[Index])
{
LoadRequests[Index].MergeWith(TileRequest);
FLoadRequestFlags& Flags = LoadRequestFlags[Index];
check(bStreamingRequest == Flags.bStreaming);
if (!Flags.bLocked)
{
Flags.bLocked = true;
LoadRequestCount[Index] = MAX_uint16;
++NumLockRequests;
}
LoadRequestGroupMask[Index] |= GroupMask;
return Index;
}
}
if (NumLoadRequests < LoadRequestCapacity)
{
const uint32 Index = NumLoadRequests++;
LoadRequestHash.Add(Hash, Index);
LoadRequests[Index] = TileRequest;
LoadRequestCount[Index] = MAX_uint16;
LoadRequestGroupMask[Index] = GroupMask;
LoadRequestFlags[Index] = FLoadRequestFlags(/*bInLocked = */true, bStreamingRequest);
++NumLockRequests;
return Index;
}
return 0xffff;
}
void FUniqueRequestList::AddMappingRequest(uint16 LoadRequestIndex, uint8 ProducerPhysicalGroupIndex, uint8 SpaceID, uint8 PageTableLayerIndex, uint32 MaxLevel, uint32 vAddress, uint8 vLevel, uint8 Local_vLevel)
{
check(LoadRequestIndex < NumLoadRequests);
const FMappingRequest Request(LoadRequestIndex, ProducerPhysicalGroupIndex, SpaceID, PageTableLayerIndex, MaxLevel, vAddress, vLevel, Local_vLevel);
const uint16 Hash = static_cast<uint16>(MurmurFinalize64(Request.PackedValue));
for (uint16 Index = MappingRequestHash.First(Hash); MappingRequestHash.IsValid(Index); Index = MappingRequestHash.Next(Index))
{
if (Request == MappingRequests[Index])
{
return;
}
}
if (NumMappingRequests < MappingRequestCapacity)
{
const uint32 Index = NumMappingRequests++;
MappingRequestHash.Add(Hash, Index);
MappingRequests[Index] = Request;
}
}
void FUniqueRequestList::AddDirectMappingRequest(uint8 InSpaceID, uint16 InPhysicalSpaceID, uint8 InPageTableLayerIndex, uint32 InMaxLevel, uint32 InAddress, uint8 InLevel, uint8 InLocalLevel, uint16 InPhysicalAddress)
{
const FDirectMappingRequest Request(InSpaceID, InPhysicalSpaceID, InPageTableLayerIndex, InMaxLevel, InAddress, InLevel, InLocalLevel, InPhysicalAddress);
AddDirectMappingRequest(Request);
}
void FUniqueRequestList::AddDirectMappingRequest(const FDirectMappingRequest& Request)
{
const uint16 Hash = Murmur32({ Request.PackedValue[0], Request.PackedValue[1], Request.PackedValue[2] });
for (uint16 Index = DirectMappingRequestHash.First(Hash); DirectMappingRequestHash.IsValid(Index); Index = DirectMappingRequestHash.Next(Index))
{
if (Request == DirectMappingRequests[Index])
{
return;
}
}
if (NumDirectMappingRequests < DirectMappingRequestCapacity)
{
const uint32 Index = NumDirectMappingRequests++;
DirectMappingRequestHash.Add(Hash, Index);
DirectMappingRequests[Index] = Request;
}
}
void FUniqueRequestList::AddContinuousUpdateRequest(const FVirtualTextureLocalTileRequest& Request)
{
const uint16 Hash = Request.GetMurmurHash();
for (uint16 Index = ContinuousUpdateRequestHash.First(Hash); ContinuousUpdateRequestHash.IsValid(Index); Index = ContinuousUpdateRequestHash.Next(Index))
{
if (Request == ContinuousUpdateRequests[Index])
{
ContinuousUpdateRequests[Index].MergeWith(Request);
return;
}
}
if (NumContinuousUpdateRequests < ContinuousUpdateRequestCapacity)
{
const uint32 Index = NumContinuousUpdateRequests++;
ContinuousUpdateRequestHash.Add(Hash, Index);
ContinuousUpdateRequests[Index] = Request;
}
}
void FUniqueRequestList::AddAdaptiveAllocationRequest(uint32 Request)
{
if (NumAdaptiveAllocationRequests < AdaptiveAllocationRequestCapacity)
{
AdaptiveAllocationsRequests[NumAdaptiveAllocationRequests++] = Request;
}
}
void FUniqueRequestList::MergeRequests(const FUniqueRequestList* RESTRICT Other, FConcurrentLinearBulkObjectAllocator& Allocator)
{
uint16* LoadRequestIndexRemap = Allocator.MallocArray<uint16>(Other->NumLoadRequests);
for (uint32 Index = 0u; Index < Other->NumLoadRequests; ++Index)
{
if (Other->IsLocked(Index))
{
LoadRequestIndexRemap[Index] = LockLoadRequest(Other->GetLoadRequest(Index), Other->LoadRequestGroupMask[Index], Other->LoadRequestFlags[Index].bStreaming);
}
else
{
LoadRequestIndexRemap[Index] = AddLoadRequest(Other->GetLoadRequest(Index), Other->LoadRequestGroupMask[Index], Other->LoadRequestCount[Index], Other->LoadRequestFlags[Index].bStreaming);
}
}
for (uint32 Index = 0u; Index < Other->NumMappingRequests; ++Index)
{
const FMappingRequest& Request = Other->GetMappingRequest(Index);
check(Request.LoadRequestIndex < Other->NumLoadRequests);
const uint16 LoadRequestIndex = LoadRequestIndexRemap[Request.LoadRequestIndex];
if (LoadRequestIndex != 0xffff)
{
AddMappingRequest(LoadRequestIndex, Request.ProducerPhysicalGroupIndex, Request.SpaceID, Request.PageTableLayerIndex, Request.MaxLevel, Request.vAddress, Request.vLevel, Request.Local_vLevel);
}
}
for (uint32 Index = 0u; Index < Other->NumDirectMappingRequests; ++Index)
{
AddDirectMappingRequest(Other->GetDirectMappingRequest(Index));
}
for (uint32 Index = 0u; Index < Other->NumContinuousUpdateRequests; ++Index)
{
AddContinuousUpdateRequest(Other->GetContinuousUpdateRequest(Index));
}
for (uint32 Index = 0u; Index < Other->NumAdaptiveAllocationRequests; ++Index)
{
AddAdaptiveAllocationRequest(Other->GetAdaptiveAllocationRequest(Index));
}
}
void FUniqueRequestList::SortRequests(FVirtualTextureProducerCollection& Producers, FConcurrentLinearBulkObjectAllocator& Allocator, uint32 MaxNonStreamingLoadRequests, uint32 MaxStreamingLoadRequests, bool bUseCombinedLimit, bool bSortByPriority)
{
if (bUseCombinedLimit)
{
MaxNonStreamingLoadRequests += MaxStreamingLoadRequests;
MaxStreamingLoadRequests = 0;
}
// Compute priority of each load request
uint32 CheckNumLockRequests = 0u;
uint32 NumNonStreamingLockRequests = 0u;
uint32 NumStreamingNonLockRequests = 0u;
FVTRequestPriorityAndIndex* SortedKeys = Allocator.CreateArray<FVTRequestPriorityAndIndex>(NumLoadRequests);
for (uint32 i = 0u; i < NumLoadRequests; ++i)
{
const uint32 Count = LoadRequestCount[i];
const FLoadRequestFlags Flags = LoadRequestFlags[i];
// Try to load higher mips first
const FVirtualTextureLocalTileRequest& LoadRequest = GetLoadRequest(i);
const uint32 PagePriority = (Count * (1u + LoadRequest.GetTile().Local_vLevel));
const bool bStreaming = !bUseCombinedLimit && Flags.bStreaming;
if (Flags.bLocked)
{
NumNonStreamingLockRequests += bStreaming ? 0 : 1;
++CheckNumLockRequests;
}
else if (bStreaming)
{
++NumStreamingNonLockRequests;
}
SortedKeys[i] = FVTRequestPriorityAndIndex(
i,
/*bInLocked = */(Flags.bLocked != 0),
/*bInStreaming = */bStreaming,
bSortByPriority ? LoadRequest.GetProducerPriority() : static_cast<EVTProducerPriority>(0),
bSortByPriority ? LoadRequest.GetInvalidatePriority() : static_cast<EVTInvalidatePriority>(0),
PagePriority);
}
checkSlow(CheckNumLockRequests == NumLockRequests);
// Sort so highest priority requests are at the front of the list.
// Important note : the rest of the algorithm assumes locked requests come first, then streaming requests, then the rest :
Algo::Sort(MakeArrayView(SortedKeys, NumLoadRequests));
// Clamp number of load requests to maximum, but also ensure all lock requests are considered
const uint32 NumStreamingLockRequests = NumLockRequests - NumNonStreamingLockRequests;
const uint32 NumStreamingRequests = NumStreamingNonLockRequests + NumStreamingLockRequests;
const uint32 NumNonStreamingRequests = NumLoadRequests - NumStreamingRequests;
const uint32 NewNumNonStreamingRequests = FMath::Min(NumNonStreamingRequests, FMath::Max(NumNonStreamingLockRequests, MaxNonStreamingLoadRequests));
const uint32 NewNumStreamingRequests = FMath::Min(NumStreamingRequests, FMath::Max(NumStreamingLockRequests, MaxStreamingLoadRequests));
const uint32 NewNumLoadRequests = NewNumNonStreamingRequests + NewNumStreamingRequests;
// Re-index load request list, using sorted indices
FVirtualTextureLocalTileRequest* SortedLoadRequests = Allocator.CreateArray<FVirtualTextureLocalTileRequest>(NewNumLoadRequests);
uint8* SortedGroupMask = Allocator.MallocArray<uint8>(NewNumLoadRequests);
FLoadRequestFlags* SortedFlags = Allocator.MallocArray<FLoadRequestFlags>(NewNumLoadRequests);
uint16* LoadIndexToSortedLoadIndex = Allocator.MallocArray<uint16>(NumLoadRequests);
FMemory::Memset(LoadIndexToSortedLoadIndex, 0xff, NumLoadRequests * sizeof(uint16));
uint32 WriteIndex = 0;
auto CopyRequestToSorted = [this, &WriteIndex, SortedKeys, SortedLoadRequests, SortedGroupMask, SortedFlags, LoadIndexToSortedLoadIndex](uint32 SortedIndex)
{
const uint32 OldIndex = SortedKeys[SortedIndex].Index;
checkSlow(OldIndex < NumLoadRequests);
SortedLoadRequests[WriteIndex] = LoadRequests[OldIndex];
SortedGroupMask[WriteIndex] = LoadRequestGroupMask[OldIndex];
SortedFlags[WriteIndex] = LoadRequestFlags[OldIndex];
LoadIndexToSortedLoadIndex[OldIndex] = WriteIndex;
++WriteIndex;
};
for (uint32 SortedIndex = 0u; SortedIndex < NumLockRequests; ++SortedIndex)
{
CopyRequestToSorted(SortedIndex);
checkfSlow((SortedKeys[SortedIndex].GetPriorityKey().Locked != 0) && SortedFlags[WriteIndex - 1].bLocked, TEXT("If this asserts, then the sorting is invalid : the code assumes locked requests are the ones at the beginning of the sorted list"));
}
for (uint32 i = 0u; i < NewNumStreamingRequests - NumStreamingLockRequests; ++i)
{
const uint32 SortedIndex = NumLockRequests + i;
CopyRequestToSorted(SortedIndex);
checkfSlow((SortedKeys[SortedIndex].GetPriorityKey().Streaming != 0) && !SortedFlags[WriteIndex - 1].bLocked, TEXT("If this asserts, then the sorting is invalid : the code assumes locked requests are the ones at the beginning of the sorted list"));
}
for (uint32 i = 0u; i < NewNumNonStreamingRequests - NumNonStreamingLockRequests; ++i)
{
const uint32 SortedIndex = NumLockRequests + NumStreamingNonLockRequests + i;
CopyRequestToSorted(SortedIndex);
checkfSlow((SortedKeys[SortedIndex].GetPriorityKey().Streaming == 0) && !SortedFlags[WriteIndex - 1].bLocked, TEXT("If this asserts, then the sorting is invalid : the code assumes locked requests are the ones at the beginning of the sorted list"));
}
check(NewNumLoadRequests == WriteIndex);
LoadRequests = SortedLoadRequests;
LoadRequestGroupMask = SortedGroupMask;
LoadRequestFlags = SortedFlags;
// Remap LoadRequest indices for all the mapping requests
// Can discard any mapping request that refers to a LoadRequest that's no longer being performed this frame
uint32 NewNumMappingRequests = 0u;
for (uint32 i = 0u; i < NumMappingRequests; ++i)
{
FMappingRequest Request = GetMappingRequest(i);
checkSlow(Request.LoadRequestIndex < NumLoadRequests);
const uint16 SortedLoadIndex = LoadIndexToSortedLoadIndex[Request.LoadRequestIndex];
if (SortedLoadIndex != 0xffff)
{
check(SortedLoadIndex < NewNumLoadRequests);
Request.LoadRequestIndex = SortedLoadIndex;
MappingRequests[NewNumMappingRequests++] = Request;
}
}
NumLoadRequests = NewNumLoadRequests;
NumNonStreamingLoadRequests = NewNumNonStreamingRequests;
check(!bUseCombinedLimit || NumLoadRequests == NumNonStreamingLoadRequests);
NumMappingRequests = NewNumMappingRequests;
}