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

219 lines
7.2 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "VirtualTextureShared.h"
#include "Containers/BinaryHeap.h"
#include "Containers/HashTable.h"
class FVirtualTextureSystem;
class FVirtualTextureSpace;
class FVirtualTexturePhysicalSpace;
// 4k x 4k virtual pages
// 256 x 256 physical pages
union FTexturePage
{
FTexturePage() : Packed(~0u) {}
FTexturePage(uint8 InLogSize, uint32 InAddress) : vAddress(InAddress), vLogSize(InLogSize) {}
uint32 Packed;
struct
{
// Address is Morton order, relative to mip 0
uint32 vAddress : 24;
uint32 vLogSize : 8;
};
};
static_assert(sizeof(FTexturePage) == sizeof(uint32), "Bad packing");
FORCEINLINE bool operator==(const FTexturePage& Lhs, const FTexturePage& Rhs) { return Lhs.Packed == Rhs.Packed; }
FORCEINLINE bool operator!=(const FTexturePage& Lhs, const FTexturePage& Rhs) { return Lhs.Packed != Rhs.Packed; }
union FPhysicalSpaceIDAndAddress
{
FPhysicalSpaceIDAndAddress() : Packed(~0u) {}
FPhysicalSpaceIDAndAddress(uint16 InPhysicalSpaceID, uint16 InAddress) : PhysicalSpaceID(InPhysicalSpaceID), pAddress(InAddress) {}
uint32 Packed;
struct
{
uint16 PhysicalSpaceID;
uint16 pAddress;
};
};
FORCEINLINE bool operator==(const FPhysicalSpaceIDAndAddress& Lhs, const FPhysicalSpaceIDAndAddress& Rhs) { return Lhs.Packed == Rhs.Packed; }
FORCEINLINE bool operator!=(const FPhysicalSpaceIDAndAddress& Lhs, const FPhysicalSpaceIDAndAddress& Rhs) { return Lhs.Packed != Rhs.Packed; }
struct FMappedTexturePage
{
FTexturePage Page;
uint32 pAddress : 16;
uint32 PhysicalSpaceID : 12;
uint32 Local_vLevel : 4;
};
/**
* Manages a single layer of a VT page table, contains mappings of virtual->physical address
* Pages should not be directly mapped/unmapped from this class, this should instead go through FTexturePagePool
* In the context of page mappings, vLogSize and vLevel refer to 2 similar but slightly different things
* - vLogSize is the mip level of the virtual address space being mapped (from the allocated VT)
* - vLevel is the mip level of the producer that's being mapped (somethings called Local_vLevel)
* - These are often the same value, but can be different in certain situations.
* For example when unmapping a page, the ancestor page with a higher vLevel is mapped to the same address at vLogSize
* When different layers have different sizes, mip bias will cause lower vLevel pages to be mapped to address at vLogSize
*/
class FTexturePageMap
{
public:
FTexturePageMap();
~FTexturePageMap();
void Initialize(uint32 InSize, uint32 InLayerIndex, uint32 InDimensions);
uint32 GetSize() const { return Pages.Num(); }
/**
* Find the physical address for the given virtual address.
* Returns ~0u if not found.
*/
uint32 FindPageAddress(uint8 vLogSize, uint32 vAddress) const;
FPhysicalSpaceIDAndAddress FindPagePhysicalSpaceIDAndAddress(const FTexturePage& CheckPage, uint16 Hash) const;
FPhysicalSpaceIDAndAddress FindPagePhysicalSpaceIDAndAddress(uint8 vLogSize, uint32 vAddress) const;
/**
* Find the best matching physical address along the mipmap fall back chain for the given virtual address.
* Returns ~0u if none found at all.
*/
uint32 FindNearestPageAddress(uint8 vLogSize, uint32 vAddress) const;
uint32 FindNearestPageLevel(uint8 vLogSize, uint32 vAddress) const;
/**
* Unmap the physical address from any virtual address it was mapped to before.
*/
void UnmapPage(FVirtualTextureSystem* System, FVirtualTextureSpace* Space, uint8 vLogSize, uint32 vAddress, bool bMapAncestorPage);
/**
* Map the physical address to a specific virtual address.
*/
void MapPage(FVirtualTextureSpace* Space, FVirtualTexturePhysicalSpace* PhysicalSpace, uint32 PackedProducerHandle, uint8 MaxLevel, uint8 vLogSize, uint32 vAddress, uint8 Local_vLevel, uint16 pAddress);
void GetMappedPagesInRange(uint32 vAddress, uint32 Width, uint32 Height, TArray<FMappedTexturePage>& OutMappedPages) const;
void RefreshEntirePageTable(FVirtualTextureSystem* System, TArray< FPageTableUpdate >* Output);
void ExpandPageTableUpdatePainters(FVirtualTextureSystem* System, FPageTableUpdate Update, TArray< FPageTableUpdate >* Output);
void ExpandPageTableUpdateMasked(FVirtualTextureSystem* System, FPageTableUpdate Update, TArray< FPageTableUpdate >* Output);
void InvalidateUnmappedRootPage(FVirtualTextureSpace* Space, FVirtualTexturePhysicalSpace* PhysicalSpace, uint32 PackedProducerHandle, uint8 MaxLevel, uint8 vLogSize, uint32 vAddress, uint8 Local_vLevel);
private:
void BuildSortedKeys();
void ReleaseUnmappedPages();
uint32 LowerBound(uint32 Min, uint32 Max, uint32 SearchKey, uint32 Mask) const;
uint32 UpperBound(uint32 Min, uint32 Max, uint32 SearchKey, uint32 Mask) const;
uint64 EqualRange(uint32 Min, uint32 Max, uint32 SearchKey, uint32 Mask) const;
uint32 FindPageIndex(uint8 vLogSize, uint32 vAddress) const;
uint32 FindNearestPageIndex(uint8 vLogSize, uint32 vAddress, uint8 MaxLevel) const;
uint32 LayerIndex;
uint32 vDimensions;
enum EPageListHead
{
PageListHead_Free,
PageListHead_Mapped,
PageListHead_Unmapped,
PageListHead_Count,
};
struct FPageEntry
{
FTexturePage Page;
uint32 NextIndex;
uint32 PrevIndex;
union
{
uint64 Packed;
struct
{
uint32 PackedProducerHandle;
uint32 pAddress : 16;
uint32 PhysicalSpaceID : 8;
uint32 MaxLevel : 4;
uint32 Local_vLevel : 4;
};
};
};
void RemovePageFromList(uint32 Index)
{
FPageEntry& Page = Pages[Index];
Pages[Page.PrevIndex].NextIndex = Page.NextIndex;
Pages[Page.NextIndex].PrevIndex = Page.PrevIndex;
Page.NextIndex = Page.PrevIndex = Index;
}
void AddPageToList(uint32 HeadIndex, uint32 Index)
{
FPageEntry& Head = Pages[HeadIndex];
FPageEntry& Page = Pages[Index];
check(Index >= PageListHead_Count); // make sure we're not trying to add a list head to another list
// make sure we're not currently in any list
check(Page.NextIndex == Index);
check(Page.PrevIndex == Index);
Page.NextIndex = HeadIndex;
Page.PrevIndex = Head.PrevIndex;
Pages[Head.PrevIndex].NextIndex = Index;
Head.PrevIndex = Index;
}
uint32 AcquirePage()
{
FPageEntry& FreeHead = Pages[PageListHead_Free];
uint32 Index = FreeHead.NextIndex;
if (Index != 0u)
{
RemovePageFromList(Index);
return Index;
}
Index = Pages.AddDefaulted();
FPageEntry& Page = Pages[Index];
Page.NextIndex = Page.PrevIndex = Index;
return Index;
}
TArray<FPageEntry> Pages;
FHashTable HashTable;
uint32 MappedPageCount;
TArray< uint32 > UnsortedKeys;
TArray< FPhysicalSpaceIDAndAddress > UnsortedAddresses;
TArray< uint32 > SortedKeys;
TArray< FPhysicalSpaceIDAndAddress > SortedAddresses;
bool SortedKeysDirty;
TArray< uint32 > SortedSubIndexes;
TArray< uint64 > SortedAddIndexes;
};
inline FPhysicalSpaceIDAndAddress FTexturePageMap::FindPagePhysicalSpaceIDAndAddress(const FTexturePage& CheckPage, uint16 Hash) const
{
for (uint32 PageIndex = HashTable.First(Hash); HashTable.IsValid(PageIndex); PageIndex = HashTable.Next(PageIndex))
{
const FPageEntry& Entry = Pages[PageIndex];
if (Entry.Page == CheckPage)
{
return FPhysicalSpaceIDAndAddress(Entry.PhysicalSpaceID, Entry.pAddress);
}
}
return FPhysicalSpaceIDAndAddress();
}