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

550 lines
22 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "AllocatedVirtualTexture.h"
#include "Misc/StringBuilder.h"
#include "VT/VirtualTextureScalability.h"
#include "VT/VirtualTextureSystem.h"
#include "VT/VirtualTextureSpace.h"
#include "VT/VirtualTexturePhysicalSpace.h"
bool GSupport16BitPageTable = true;
static FAutoConsoleVariableRef CVarVTSupport16BitPageTable(
TEXT("r.VT.Support16BitPageTable"),
GSupport16BitPageTable,
TEXT("Enable support for 16 bit page table entries.\n")
TEXT("This can reduce page table memory when only 16bit addressing is needed.\n")
TEXT("But this can increase the number of page table spaces required when a mixture of 16bit and 32bit addressing is needed.\n")
TEXT("Defaults on.\n"),
ECVF_ReadOnly);
FAllocatedVirtualTexture::FAllocatedVirtualTexture(
FRHICommandListBase& RHICmdList,
FVirtualTextureSystem* InSystem,
uint32 InFrame,
const FAllocatedVTDescription& InDesc,
FVirtualTextureProducer* const* InProducers,
uint32 InBlockWidthInTiles,
uint32 InBlockHeightInTiles,
uint32 InWidthInBlocks,
uint32 InHeightInBlocks,
uint32 InDepthInTiles)
: IAllocatedVirtualTexture(InDesc, InBlockWidthInTiles, InBlockHeightInTiles, InWidthInBlocks, InHeightInBlocks, InDepthInTiles)
, FrameAllocated(InFrame)
, Space(nullptr)
{
FMemory::Memzero(TextureLayers);
FMemory::Memzero(FallbackColorPerTextureLayer);
bool bContinuousUpdate = false;
for (uint32 LayerIndex = 0u; LayerIndex < Description.NumTextureLayers; ++LayerIndex)
{
FVirtualTextureProducer const* Producer = InProducers[LayerIndex];
// Can't have missing entries for null producers if we're not merging duplicate layers
if (Producer || !Description.bShareDuplicateLayers)
{
const uint32 UniqueProducerIndex = AddUniqueProducer(InDesc.ProducerHandle[LayerIndex], Producer);
const int32 ProducerLayerIndex = InDesc.ProducerLayerIndex[LayerIndex];
uint32 ProducerPhysicalGroupIndex = 0u;
FVirtualTexturePhysicalSpace* PhysicalSpace = nullptr;
if (Producer)
{
ProducerPhysicalGroupIndex = Producer->GetPhysicalGroupIndexForTextureLayer(ProducerLayerIndex);
PhysicalSpace = Producer->GetPhysicalSpaceForPhysicalGroup(ProducerPhysicalGroupIndex);
bContinuousUpdate |= Producer->GetDescription().bContinuousUpdate;
}
const uint32 UniquePhysicalSpaceIndex = AddUniquePhysicalSpace(RHICmdList, PhysicalSpace, UniqueProducerIndex, ProducerPhysicalGroupIndex);
UniquePageTableLayers[UniquePhysicalSpaceIndex].ProducerTextureLayerMask |= 1 << ProducerLayerIndex;
const uint8 PageTableLayerLocalIndex = UniquePageTableLayers[UniquePhysicalSpaceIndex].TextureLayerCount++;
TextureLayers[LayerIndex].UniquePageTableLayerIndex = UniquePhysicalSpaceIndex;
TextureLayers[LayerIndex].PhysicalTextureIndex = PageTableLayerLocalIndex;
if (Producer)
{
FallbackColorPerTextureLayer[LayerIndex] = Producer->GetDescription().LayerFallbackColor[ProducerLayerIndex].ToFColor(false).DWColor();
}
}
}
// Must have at least 1 valid layer/producer
check(UniqueProducers.Num() > 0u);
// Max level of overall allocated VT is limited by size in tiles
// With multiple layers of different sizes, some layers may have mips smaller than a single tile
// We can either use the Min or Max of Width/Height to determine the number of mips
// - Using Max will allow more mips for rectangular VTs, which could potentially reduce aliasing in certain situations
// - Using Min will relax alignment requirements for the page table allocator, which will tend to reduce overall VRAM usage
MaxLevel = FMath::Min(MaxLevel, FMath::CeilLogTwo(FMath::Min(GetWidthInTiles(), GetHeightInTiles())));
MaxLevel = FMath::Min(MaxLevel, VIRTUALTEXTURE_LOG2_MAX_PAGETABLE_SIZE - 1u);
// Get the persistent hash that we will use for identification of AllocatedVT objects across runs.
PersistentHash = CalculatePersistentHash(InDesc, InProducers);
// Lock tiles
LockOrUnlockTiles(InSystem, true);
// Use 16bit page table entries if all physical spaces are small enough
bool bSupport16BitPageTable = GSupport16BitPageTable;
for (int32 Index = 0; Index < UniquePageTableLayers.Num(); ++Index)
{
const FVirtualTexturePhysicalSpace* PhysicalSpace = UniquePageTableLayers[Index].PhysicalSpace;
if (PhysicalSpace && !PhysicalSpace->DoesSupport16BitPageTable())
{
bSupport16BitPageTable = false;
break;
}
}
FVTSpaceDescription SpaceDesc;
SpaceDesc.Dimensions = InDesc.Dimensions;
SpaceDesc.NumPageTableLayers = UniquePageTableLayers.Num();
SpaceDesc.TileSize = InDesc.TileSize;
SpaceDesc.TileBorderSize = InDesc.TileBorderSize;
SpaceDesc.bPrivateSpace = InDesc.bPrivateSpace;
SpaceDesc.bContinuousUpdate = bContinuousUpdate;
SpaceDesc.MaxSpaceSize = InDesc.MaxSpaceSize > 0 ? InDesc.MaxSpaceSize : SpaceDesc.MaxSpaceSize;
SpaceDesc.IndirectionTextureSize = InDesc.IndirectionTextureSize;
SpaceDesc.PageTableFormat = bSupport16BitPageTable ? EVTPageTableFormat::UInt16 : EVTPageTableFormat::UInt32;
Space = InSystem->AcquireSpace(RHICmdList, SpaceDesc, InDesc.ForceSpaceID, this);
SpaceID = Space->GetID();
PageTableFormat = Space->GetPageTableFormat();
}
FAllocatedVirtualTexture::~FAllocatedVirtualTexture()
{
}
void FAllocatedVirtualTexture::AssignVirtualAddress(uint32 vAddress)
{
checkf(VirtualAddress == ~0u, TEXT("Trying to assign vAddress to AllocatedVT, already assigned"));
check(vAddress != ~0u);
VirtualAddress = vAddress;
VirtualPageX = FMath::ReverseMortonCode2(vAddress);
VirtualPageY = FMath::ReverseMortonCode2(vAddress >> 1);
}
void FAllocatedVirtualTexture::Destroy(FVirtualTextureSystem* System)
{
check(NumRefs == 0);
// Unlock any locked tiles
LockOrUnlockTiles(System, false);
// Physical pool needs to evict all pages that belong to this VT
{
const uint32 WidthInTiles = GetWidthInTiles();
const uint32 HeightInTiles = GetHeightInTiles();
TArray<FVirtualTexturePhysicalSpace*> UniquePhysicalSpaces;
for (int32 PageTableIndex = 0u; PageTableIndex < UniquePageTableLayers.Num(); ++PageTableIndex)
{
if (UniquePageTableLayers[PageTableIndex].PhysicalSpace)
{
UniquePhysicalSpaces.Add(UniquePageTableLayers[PageTableIndex].PhysicalSpace);
}
}
for (FVirtualTexturePhysicalSpace* PhysicalSpace : UniquePhysicalSpaces)
{
PhysicalSpace->GetPagePool().UnmapAllPagesForSpace(System, Space->GetID(), VirtualAddress, WidthInTiles, HeightInTiles, MaxLevel);
}
for (int32 PageTableIndex = 0u; PageTableIndex < UniquePageTableLayers.Num(); ++PageTableIndex)
{
if (UniquePageTableLayers[PageTableIndex].PhysicalSpace)
{
if (UniquePageTableLayers[PageTableIndex].PhysicalSpace->ReleaseResourceRef() == 0)
{
UniquePageTableLayers[PageTableIndex].PhysicalSpace->ReleaseResource();
}
UniquePageTableLayers[PageTableIndex].PhysicalSpace.SafeRelease();
}
}
#if DO_CHECK
for (uint32 LayerIndex = 0; LayerIndex < Space->GetNumPageTableLayers(); ++LayerIndex)
{
const FTexturePageMap& PageMap = Space->GetPageMapForPageTableLayer(LayerIndex);
TArray<FMappedTexturePage> MappedPages;
PageMap.GetMappedPagesInRange(VirtualAddress, WidthInTiles, HeightInTiles, MappedPages);
if (MappedPages.Num() > 0)
{
TStringBuilder<2048> Message;
Message.Appendf(TEXT("Mapped pages remain after releasing AllocatedVT - vAddress: %d, Size: %d x %d, PhysicalSpaces: ["), VirtualAddress, WidthInTiles, HeightInTiles);
for (FVirtualTexturePhysicalSpace* PhysicalSpace : UniquePhysicalSpaces)
{
Message.Appendf(TEXT("%d "), PhysicalSpace->GetID());
}
Message.Appendf(TEXT("], MappedPages: ["));
for (const FMappedTexturePage& MappedPage : MappedPages)
{
Message.Appendf(TEXT("(vAddress: %d, PhysicalSpace: %d) "), MappedPage.Page.vAddress, MappedPage.PhysicalSpaceID);
}
Message.Appendf(TEXT("]"));
UE_LOG(LogVirtualTexturing, Warning, TEXT("%s"), Message.ToString());
}
}
#endif // DO_CHECK
}
Space->FreeVirtualTexture(this);
System->ReleaseSpace(Space);
}
void FAllocatedVirtualTexture::LockOrUnlockTiles(FVirtualTextureSystem* InSystem, bool bLock) const
{
// (Un)Lock lowest resolution mip from each producer
// Depending on the block dimensions of the producers that make up this allocated VT, different allocated VTs may need to lock different low resolution mips from the same producer
// In the common case where block dimensions match, same mip will be locked by all allocated VTs that make use of the same producer
for (int32 ProducerIndex = 0u; ProducerIndex < UniqueProducers.Num(); ++ProducerIndex)
{
FVirtualTextureProducerHandle ProducerHandle = UniqueProducers[ProducerIndex].Handle;
FVirtualTextureProducer* Producer = InSystem->FindProducer(ProducerHandle);
if (Producer && Producer->GetDescription().bPersistentHighestMip)
{
const uint32 MipBias = UniqueProducers[ProducerIndex].MipBias;
check(MipBias <= MaxLevel);
const uint32 Local_vLevel = MaxLevel - MipBias;
checkf(Local_vLevel <= Producer->GetMaxLevel(), TEXT("Invalid Local_vLevel %d for VT producer %s, Producer MaxLevel %d, MipBias %d, AllocatedVT MaxLevel %d"),
Local_vLevel,
*Producer->GetName().ToString(),
Producer->GetMaxLevel(),
MipBias,
MaxLevel);
const uint32 MipScaleFactor = (1u << Local_vLevel);
const uint32 RootWidthInTiles = FMath::DivideAndRoundUp(Producer->GetWidthInTiles(), MipScaleFactor);
const uint32 RootHeightInTiles = FMath::DivideAndRoundUp(Producer->GetHeightInTiles(), MipScaleFactor);
for (uint32 TileY = 0u; TileY < RootHeightInTiles; ++TileY)
{
for (uint32 TileX = 0u; TileX < RootWidthInTiles; ++TileX)
{
const uint32 Local_vAddress = FMath::MortonCode2(TileX) | (FMath::MortonCode2(TileY) << 1);
FVirtualTextureLocalTile Tile(ProducerHandle, Local_vAddress, Local_vLevel);
const uint32 LocalMipBias = Producer->GetVirtualTexture()->GetLocalMipBias(Local_vLevel, Local_vAddress);
if (LocalMipBias > 0u)
{
Tile.Local_vAddress >>= (LocalMipBias * Description.Dimensions);
Tile.Local_vLevel += LocalMipBias;
}
if (bLock)
{
InSystem->LockTile(Tile);
}
else
{
InSystem->UnlockTile(Tile, Producer);
}
}
}
}
}
}
bool FAllocatedVirtualTexture::TryMapLockedTiles(FVirtualTextureSystem* InSystem) const
{
bool bHasMissingTiles = false;
for (int32 PageTableLayerIndex = 0u; PageTableLayerIndex < UniquePageTableLayers.Num(); ++PageTableLayerIndex)
{
const FPageTableLayerDesc& PageTableLayer = UniquePageTableLayers[PageTableLayerIndex];
const FProducerDesc& UniqueProducer = UniqueProducers[PageTableLayer.UniqueProducerIndex];
const FVirtualTextureProducer* Producer = InSystem->FindProducer(UniqueProducer.Handle);
if (!Producer)
{
continue;
}
const uint32 WidthInTiles = Producer->GetWidthInTiles();
const uint32 HeightInTiles = Producer->GetHeightInTiles();
const uint32 Local_vLevel = FMath::Min(Producer->GetMaxLevel(), MaxLevel - UniqueProducer.MipBias);
const uint32 MipScaleFactor = (1u << Local_vLevel);
const uint32 RootWidthInTiles = FMath::DivideAndRoundUp(WidthInTiles, MipScaleFactor);
const uint32 RootHeightInTiles = FMath::DivideAndRoundUp(HeightInTiles, MipScaleFactor);
FTexturePagePool& PagePool = PageTableLayer.PhysicalSpace->GetPagePool();
FTexturePageMap& PageMap = Space->GetPageMapForPageTableLayer(PageTableLayerIndex);
uint32 NumNonResidentPages = 0u;
for (uint32 TileY = 0u; TileY < RootHeightInTiles; ++TileY)
{
for (uint32 TileX = 0u; TileX < RootWidthInTiles; ++TileX)
{
const uint32 vAddress = FMath::MortonCode2(VirtualPageX + (TileX << MaxLevel)) | (FMath::MortonCode2(VirtualPageY + (TileY << MaxLevel)) << 1);
uint32 pAddress = PageMap.FindPageAddress(MaxLevel, vAddress);
if (pAddress == ~0u)
{
uint32 Local_vAddress = FMath::MortonCode2(TileX) | (FMath::MortonCode2(TileY) << 1);
const uint32 LocalMipBias = Producer->GetVirtualTexture()->GetLocalMipBias(Local_vLevel, Local_vAddress);
Local_vAddress >>= (LocalMipBias * Description.Dimensions);
pAddress = PagePool.FindPageAddress(UniqueProducer.Handle, PageTableLayer.ProducerPhysicalGroupIndex, Local_vAddress, Local_vLevel + LocalMipBias);
if (pAddress != ~0u)
{
PagePool.MapPage(Space, PageTableLayer.PhysicalSpace, PageTableLayerIndex, MaxLevel, MaxLevel, vAddress, MaxLevel + LocalMipBias, pAddress);
}
else
{
bHasMissingTiles = true;
// Mark page table entry as invalid if we can't map.
PageMap.InvalidateUnmappedRootPage(Space, PageTableLayer.PhysicalSpace, PageTableLayerIndex, MaxLevel, MaxLevel, vAddress, MaxLevel + LocalMipBias);
}
}
}
}
}
// Display a warning message if we've failed to map pages for this after a set number of frames
// Generally there should be no delay, but if the system is saturated, it's possible that locked pages may not be loaded immediately
if (bHasMissingTiles && InSystem->GetFrame() > FrameAllocated + 30u)
{
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.VT.Verbose"));
if (CVar->GetValueOnRenderThread())
{
UE_LOG(LogVirtualTexturing, Warning, TEXT("Failed to map lowest resolution mip for AllocatedVT %s (%d frames)"), *Description.Name.ToString(), InSystem->GetFrame() - FrameAllocated);
}
}
return !bHasMissingTiles;
}
uint32 FAllocatedVirtualTexture::CalculatePersistentHash(FAllocatedVTDescription const& InDesc, FVirtualTextureProducer* const* InProducers) const
{
uint32 Hash = 0;
for (uint32 LayerIndex = 0u; LayerIndex < InDesc.NumTextureLayers; ++LayerIndex)
{
FVirtualTextureProducer const* Producer = InProducers[LayerIndex];
if (Producer != nullptr)
{
Hash = HashCombine(Hash, LayerIndex);
Hash = HashCombine(Hash, Producer->GetDescription().FullNameHash);
}
}
Hash = HashCombine(Hash, GetTypeHash(InDesc.TileSize));
Hash = HashCombine(Hash, GetTypeHash(InDesc.TileBorderSize));
Hash = HashCombine(Hash, GetTypeHash(InDesc.Dimensions));
Hash = HashCombine(Hash, GetTypeHash(InDesc.NumTextureLayers));
Hash = HashCombine(Hash, GetTypeHash(InDesc.PackedFlags));
return Hash;
}
uint32 FAllocatedVirtualTexture::AddUniqueProducer(FVirtualTextureProducerHandle const& InHandle, const FVirtualTextureProducer* InProducer)
{
for (int32 Index = 0u; Index < UniqueProducers.Num(); ++Index)
{
if (UniqueProducers[Index].Handle == InHandle)
{
return Index;
}
}
const uint32 Index = UniqueProducers.AddDefaulted();
check(Index < VIRTUALTEXTURE_SPACE_MAXLAYERS);
uint32 MipBias = 0u;
if (InProducer)
{
const FVTProducerDescription& ProducerDesc = InProducer->GetDescription();
// maybe these values should just be set by producers, rather than also set on AllocatedVT desc
check(ProducerDesc.Dimensions == Description.Dimensions);
check(ProducerDesc.TileSize == Description.TileSize);
check(ProducerDesc.TileBorderSize == Description.TileBorderSize);
const uint32 MipBiasX = FMath::CeilLogTwo(BlockWidthInTiles / ProducerDesc.BlockWidthInTiles);
const uint32 MipBiasY = FMath::CeilLogTwo(BlockHeightInTiles / ProducerDesc.BlockHeightInTiles);
check(ProducerDesc.BlockWidthInTiles << MipBiasX == BlockWidthInTiles);
check(ProducerDesc.BlockHeightInTiles << MipBiasY == BlockHeightInTiles);
// If the producer aspect ratio doesn't match the aspect ratio for the AllocatedVT, there's no way to choose a 100% mip bias
// By chossing the minimum of X/Y bias, we'll effectively crop this producer to match the aspect ratio of the AllocatedVT
// This case can happen as base materials will choose to group VTs together into a stack as long as all the textures assigned in the base material share the same aspect ratio
// But it's possible for a MI to overide some of thse textures such that the aspect ratios no longer match
// This will be fine for some cases, especially if the common case where the mismatched texture is a small dummy texture with a constant color
MipBias = FMath::Min(MipBiasX, MipBiasY);
MaxLevel = FMath::Max<uint32>(MaxLevel, ProducerDesc.MaxLevel + MipBias);
}
UniqueProducers[Index].Handle = InHandle;
UniqueProducers[Index].MipBias = MipBias;
return Index;
}
uint32 FAllocatedVirtualTexture::AddUniquePhysicalSpace(FRHICommandListBase& InRHICmdList, FVirtualTexturePhysicalSpace* InPhysicalSpace, uint32 InUniqueProducerIndex, uint32 InProducerPhysicalSpaceIndex)
{
if (Description.bShareDuplicateLayers)
{
for (int32 Index = 0u; Index < UniquePageTableLayers.Num(); ++Index)
{
if (UniquePageTableLayers[Index].PhysicalSpace == InPhysicalSpace &&
UniquePageTableLayers[Index].UniqueProducerIndex == InUniqueProducerIndex &&
UniquePageTableLayers[Index].ProducerPhysicalGroupIndex == InProducerPhysicalSpaceIndex)
{
return Index;
}
}
}
const uint32 Index = UniquePageTableLayers.AddDefaulted();
check(Index < VIRTUALTEXTURE_SPACE_MAXLAYERS);
UniquePageTableLayers[Index].PhysicalSpace = InPhysicalSpace;
UniquePageTableLayers[Index].UniqueProducerIndex = InUniqueProducerIndex;
UniquePageTableLayers[Index].ProducerPhysicalGroupIndex = InProducerPhysicalSpaceIndex;
UniquePageTableLayers[Index].ProducerTextureLayerMask = 0;
UniquePageTableLayers[Index].TextureLayerCount = 0;
if (InPhysicalSpace && InPhysicalSpace->AddResourceRef() == 1)
{
InPhysicalSpace->InitResource(InRHICmdList);
}
return Index;
}
uint32 FAllocatedVirtualTexture::GetNumPageTableTextures() const
{
return Space->GetNumPageTableTextures();
}
FRHITexture* FAllocatedVirtualTexture::GetPageTableTexture(uint32 InPageTableIndex) const
{
return Space->GetPageTableTexture(InPageTableIndex);
}
FRHITexture* FAllocatedVirtualTexture::GetPageTableIndirectionTexture() const
{
return Space->GetPageTableIndirectionTexture();
}
uint32 FAllocatedVirtualTexture::GetPhysicalTextureSize(uint32 InLayerIndex) const
{
if (InLayerIndex < Description.NumTextureLayers)
{
const FVirtualTexturePhysicalSpace* PhysicalSpace = UniquePageTableLayers[TextureLayers[InLayerIndex].UniquePageTableLayerIndex].PhysicalSpace;
return PhysicalSpace ? PhysicalSpace->GetTextureSize() : 0u;
}
return 0u;
}
FRHITexture* FAllocatedVirtualTexture::GetPhysicalTexture(uint32 InLayerIndex) const
{
if (InLayerIndex < Description.NumTextureLayers)
{
if (const FVirtualTexturePhysicalSpace* PhysicalSpace = UniquePageTableLayers[TextureLayers[InLayerIndex].UniquePageTableLayerIndex].PhysicalSpace)
{
TRefCountPtr<IPooledRenderTarget> PooledRenderTarget = PhysicalSpace->GetPhysicalTexture(TextureLayers[InLayerIndex].PhysicalTextureIndex);
if (PooledRenderTarget.IsValid())
{
return PooledRenderTarget->GetRHI();
}
}
}
return nullptr;
}
FRHIShaderResourceView* FAllocatedVirtualTexture::GetPhysicalTextureSRV(uint32 InLayerIndex, bool bSRGB) const
{
if (InLayerIndex < Description.NumTextureLayers)
{
const FVirtualTexturePhysicalSpace* PhysicalSpace = UniquePageTableLayers[TextureLayers[InLayerIndex].UniquePageTableLayerIndex].PhysicalSpace;
return PhysicalSpace ? PhysicalSpace->GetPhysicalTextureSRV(TextureLayers[InLayerIndex].PhysicalTextureIndex, bSRGB) : nullptr;
}
return nullptr;
}
uint32 FAllocatedVirtualTexture::GetResidencyMipMapBiasGroup() const
{
// For correctness we should fill a 4 bit mask of groups from all layers.
// But that approach also requires extra shader maths to accumulate the max bias from all 4 groups in the mask.
// So for now we are just take the group from layer 0 and pass it up directly.
FVirtualTexturePhysicalSpace* PhysicalSpace = GetPhysicalSpaceForPageTableLayer(0);
return PhysicalSpace != nullptr ? PhysicalSpace->GetDescriptionExt().ResidencyMipMapBiasGroup : 0;
}
static inline uint32 BitcastFloatToUInt32(float v)
{
const union
{
float FloatValue;
uint32 UIntValue;
} u = { v };
return u.UIntValue;
}
void FAllocatedVirtualTexture::GetPackedPageTableUniform(FUintVector4* OutUniform) const
{
const uint32 vPageX = VirtualPageX;
const uint32 vPageY = VirtualPageY;
const uint32 vPageSize = GetVirtualTileSize();
const uint32 PageBorderSize = GetTileBorderSize();
const uint32 WidthInPages = GetWidthInTiles();
const uint32 HeightInPages = GetHeightInTiles();
const uint32 vPageTableMipBias = FMath::FloorLog2(vPageSize);
const uint32 MipBiasGroup = GetResidencyMipMapBiasGroup();
const uint32 AdaptiveLevelBias = Description.AdaptiveLevelBias;
// Here MaxAnisotropy only controls the VT mip level selection
// We don't need to limit this value based on border size, and we can add this factor in even if HW anisotropic filtering is disabled
const uint32 MaxAnisotropy = VirtualTextureScalability::GetMaxAnisotropy();
const uint32 MaxAnisotropyLog2 = (MaxAnisotropy > 0u) ? FMath::FloorLog2(MaxAnisotropy) : 0u;
// make sure everything fits in the allocated number of bits
checkSlow(vPageX < 4096u);
checkSlow(vPageY < 4096u);
checkSlow(vPageTableMipBias < 16u);
checkSlow(MipBiasGroup < 4u);
checkSlow(MaxLevel < 16u);
checkSlow(AdaptiveLevelBias < 16u);
checkSlow(SpaceID < 16u);
OutUniform[0].X = BitcastFloatToUInt32(1.0f / (float)WidthInBlocks);
OutUniform[0].Y = BitcastFloatToUInt32(1.0f / (float)HeightInBlocks);
OutUniform[0].Z = BitcastFloatToUInt32((float)WidthInPages);
OutUniform[0].W = BitcastFloatToUInt32((float)HeightInPages);
OutUniform[1].X = BitcastFloatToUInt32((float)MaxAnisotropyLog2);
OutUniform[1].Y = vPageX | (vPageY << 12) | (vPageTableMipBias << 24) | (MipBiasGroup << 28);
OutUniform[1].Z = MaxLevel | (AdaptiveLevelBias << 4);
OutUniform[1].W = (SpaceID << 28);
}
void FAllocatedVirtualTexture::GetPackedUniform(FUintVector4* OutUniform, uint32 LayerIndex) const
{
const uint32 PhysicalTextureSize = GetPhysicalTextureSize(LayerIndex);
if (PhysicalTextureSize > 0u)
{
const uint32 vPageSize = GetVirtualTileSize();
const uint32 PageBorderSize = GetTileBorderSize();
const float RcpPhysicalTextureSize = 1.0f / float(PhysicalTextureSize);
const uint32 pPageSize = vPageSize + PageBorderSize * 2u;
OutUniform->X = FallbackColorPerTextureLayer[LayerIndex];
OutUniform->Y = BitcastFloatToUInt32((float)vPageSize * RcpPhysicalTextureSize);
OutUniform->Z = BitcastFloatToUInt32((float)PageBorderSize * RcpPhysicalTextureSize);
// Pack page table format bool as sign bit on page size.
const bool bPageTableExtraBits = GetPageTableFormat() == EVTPageTableFormat::UInt32;
const float PackedSignBit = bPageTableExtraBits ? 1.f : -1.f;
OutUniform->W = BitcastFloatToUInt32((float)pPageSize * RcpPhysicalTextureSize * PackedSignBit);
}
else
{
OutUniform->X = 0u;
OutUniform->Y = 0u;
OutUniform->Z = 0u;
OutUniform->W = 0u;
}
}