You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
Revert changes from 19848183. A genuine fix was applied in 20181447 so that we don't need to apply a minimum texture size as a workaround for the original bug. #preflight 627e74b85d0fe3ffef44f61c [CL 20181952 by Jeremy Moore in ue5-main branch]
564 lines
21 KiB
C++
564 lines
21 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "VirtualTextureSpace.h"
|
|
#include "VirtualTexturePhysicalSpace.h"
|
|
#include "VirtualTextureSystem.h"
|
|
#include "SpriteIndexBuffer.h"
|
|
#include "SceneFilterRendering.h"
|
|
#include "RenderTargetPool.h"
|
|
#include "VisualizeTexture.h"
|
|
#include "CommonRenderResources.h"
|
|
#include "GlobalShader.h"
|
|
#include "PipelineStateCache.h"
|
|
#include "HAL/IConsoleManager.h"
|
|
#include "SceneUtils.h"
|
|
|
|
#include "VT/AllocatedVirtualTexture.h"
|
|
|
|
DEFINE_LOG_CATEGORY_STATIC(LogVirtualTextureSpace, Log, All);
|
|
|
|
static TAutoConsoleVariable<int32> CVarVTRefreshEntirePageTable(
|
|
TEXT("r.VT.RefreshEntirePageTable"),
|
|
0,
|
|
TEXT("Refreshes the entire page table texture every frame"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarVTMaskedPageTableUpdates(
|
|
TEXT("r.VT.MaskedPageTableUpdates"),
|
|
1,
|
|
TEXT("Masks the page table update quads to reduce pixel fill costs"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static EPixelFormat GetFormatForNumLayers(uint32 NumLayers, EVTPageTableFormat Format)
|
|
{
|
|
const bool bUse16Bits = (Format == EVTPageTableFormat::UInt16);
|
|
switch (NumLayers)
|
|
{
|
|
case 1u: return bUse16Bits ? PF_R16_UINT : PF_R32_UINT;
|
|
case 2u: return bUse16Bits ? PF_R16G16_UINT : PF_R32G32_UINT;
|
|
case 3u:
|
|
case 4u: return bUse16Bits ? PF_R16G16B16A16_UINT : PF_R32G32B32A32_UINT;
|
|
default: checkNoEntry(); return PF_Unknown;
|
|
}
|
|
}
|
|
|
|
FVirtualTextureSpace::FVirtualTextureSpace(FVirtualTextureSystem* InSystem, uint8 InID, const FVTSpaceDescription& InDesc, uint32 InSizeNeeded)
|
|
: Description(InDesc)
|
|
, Allocator(InDesc.Dimensions)
|
|
, NumRefs(0u)
|
|
, ID(InID)
|
|
, CachedPageTableWidth(0u)
|
|
, CachedPageTableHeight(0u)
|
|
, CachedNumPageTableLevels(0u)
|
|
, bNeedToAllocatePageTable(true)
|
|
, bForceEntireUpdate(false)
|
|
{
|
|
// Initialize page map with large enough capacity to handle largest possible physical texture
|
|
const uint32 PhysicalTileSize = InDesc.TileSize + InDesc.TileBorderSize * 2u;
|
|
const uint32 MaxSizeInTiles = GetMax2DTextureDimension() / PhysicalTileSize;
|
|
const uint32 MaxNumTiles = MaxSizeInTiles * MaxSizeInTiles;
|
|
for (uint32 LayerIndex = 0u; LayerIndex < InDesc.NumPageTableLayers; ++LayerIndex)
|
|
{
|
|
PhysicalPageMap[LayerIndex].Initialize(MaxNumTiles, LayerIndex, InDesc.Dimensions);
|
|
}
|
|
|
|
uint32 NumLayersToAllocate = InDesc.NumPageTableLayers;
|
|
uint32 PageTableIndex = 0u;
|
|
FMemory::Memzero(TexturePixelFormat);
|
|
while (NumLayersToAllocate > 0u)
|
|
{
|
|
const uint32 NumLayersForTexture = FMath::Min(NumLayersToAllocate, LayersPerPageTableTexture);
|
|
const EPixelFormat PixelFormat = GetFormatForNumLayers(NumLayersForTexture, InDesc.PageTableFormat);
|
|
TexturePixelFormat[PageTableIndex] = PixelFormat;
|
|
NumLayersToAllocate -= NumLayersForTexture;
|
|
++PageTableIndex;
|
|
}
|
|
|
|
Allocator.Initialize(Description.MaxSpaceSize);
|
|
|
|
bNeedToAllocatePageTableIndirection = InDesc.IndirectionTextureSize > 0;
|
|
}
|
|
|
|
FVirtualTextureSpace::~FVirtualTextureSpace()
|
|
{
|
|
}
|
|
|
|
uint32 FVirtualTextureSpace::AllocateVirtualTexture(FAllocatedVirtualTexture* VirtualTexture)
|
|
{
|
|
uint32 vAddress = Allocator.Alloc(VirtualTexture);
|
|
if (Allocator.GetAllocatedWidth() > CachedPageTableWidth || Allocator.GetAllocatedHeight() > CachedPageTableHeight)
|
|
{
|
|
bNeedToAllocatePageTable = true;
|
|
}
|
|
|
|
return vAddress;
|
|
}
|
|
|
|
void FVirtualTextureSpace::FreeVirtualTexture(FAllocatedVirtualTexture* VirtualTexture)
|
|
{
|
|
Allocator.Free(VirtualTexture);
|
|
}
|
|
|
|
void FVirtualTextureSpace::InitRHI()
|
|
{
|
|
for (uint32 TextureIndex = 0u; TextureIndex < GetNumPageTableTextures(); ++TextureIndex)
|
|
{
|
|
FTextureEntry& TextureEntry = PageTable[TextureIndex];
|
|
TextureEntry.TextureReferenceRHI = RHICreateTextureReference();
|
|
}
|
|
PageTableIndirection.TextureReferenceRHI = RHICreateTextureReference();
|
|
RHIUpdateTextureReference(PageTableIndirection.TextureReferenceRHI, GBlackUintTexture->TextureRHI);
|
|
}
|
|
|
|
void FVirtualTextureSpace::ReleaseRHI()
|
|
{
|
|
for (uint32 i = 0u; i < TextureCapacity; ++i)
|
|
{
|
|
FTextureEntry& TextureEntry = PageTable[i];
|
|
TextureEntry.TextureReferenceRHI.SafeRelease();
|
|
GRenderTargetPool.FreeUnusedResource(TextureEntry.RenderTarget);
|
|
}
|
|
|
|
PageTableIndirection.TextureReferenceRHI.SafeRelease();
|
|
GRenderTargetPool.FreeUnusedResource(PageTableIndirection.RenderTarget);
|
|
|
|
UpdateBuffer.SafeRelease();
|
|
UpdateBufferSRV.SafeRelease();
|
|
}
|
|
|
|
uint32 FVirtualTextureSpace::GetSizeInBytes() const
|
|
{
|
|
uint32 TotalSize = 0u;
|
|
for (uint32 TextureIndex = 0u; TextureIndex < GetNumPageTableTextures(); ++TextureIndex)
|
|
{
|
|
const uint32 PageTableWidth = Align(Allocator.GetAllocatedWidth(), VIRTUALTEXTURE_MIN_PAGETABLE_SIZE);
|
|
const uint32 PageTableHeight = Align(Allocator.GetAllocatedHeight(), VIRTUALTEXTURE_MIN_PAGETABLE_SIZE);
|
|
const uint32 NumPageTableLevels = FMath::FloorLog2(FMath::Max(PageTableWidth, PageTableHeight)) + 1u;
|
|
const SIZE_T TextureSize = CalcTextureSize(PageTableWidth, PageTableHeight, TexturePixelFormat[TextureIndex], NumPageTableLevels);
|
|
TotalSize += TextureSize;
|
|
}
|
|
|
|
TotalSize += CalculateImageBytes(Description.IndirectionTextureSize, Description.IndirectionTextureSize, 0, PF_R32_UINT);
|
|
|
|
return TotalSize;
|
|
}
|
|
|
|
void FVirtualTextureSpace::QueueUpdate(uint8 Layer, uint8 vLogSize, uint32 vAddress, uint8 vLevel, const FPhysicalTileLocation& pTileLocation)
|
|
{
|
|
FPageTableUpdate Update;
|
|
Update.vAddress = vAddress;
|
|
Update.pTileLocation = pTileLocation;
|
|
Update.vLevel = vLevel;
|
|
Update.vLogSize = vLogSize;
|
|
Update.Check( Description.Dimensions );
|
|
PageTableUpdates[Layer].Add( Update );
|
|
}
|
|
|
|
|
|
TGlobalResource< FSpriteIndexBuffer<8> > GQuadIndexBuffer;
|
|
|
|
class FPageTableUpdateVS : public FGlobalShader
|
|
{
|
|
DECLARE_INLINE_TYPE_LAYOUT(FPageTableUpdateVS, NonVirtual);
|
|
protected:
|
|
FPageTableUpdateVS() {}
|
|
|
|
public:
|
|
FPageTableUpdateVS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
|
|
: FGlobalShader(Initializer)
|
|
{
|
|
PageTableSize.Bind( Initializer.ParameterMap, TEXT("PageTableSize") );
|
|
FirstUpdate.Bind( Initializer.ParameterMap, TEXT("FirstUpdate") );
|
|
NumUpdates.Bind( Initializer.ParameterMap, TEXT("NumUpdates") );
|
|
UpdateBuffer.Bind( Initializer.ParameterMap, TEXT("UpdateBuffer") );
|
|
}
|
|
|
|
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
|
|
{
|
|
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5)
|
|
|| GetMaxSupportedFeatureLevel(Parameters.Platform) == ERHIFeatureLevel::ES3_1;
|
|
}
|
|
|
|
LAYOUT_FIELD(FShaderParameter, PageTableSize);
|
|
LAYOUT_FIELD(FShaderParameter, FirstUpdate);
|
|
LAYOUT_FIELD(FShaderParameter, NumUpdates);
|
|
LAYOUT_FIELD(FShaderResourceParameter, UpdateBuffer);
|
|
};
|
|
|
|
class FPageTableUpdatePS : public FGlobalShader
|
|
{
|
|
DECLARE_INLINE_TYPE_LAYOUT(FPageTableUpdatePS, NonVirtual);
|
|
protected:
|
|
FPageTableUpdatePS() {}
|
|
|
|
public:
|
|
FPageTableUpdatePS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
|
|
: FGlobalShader(Initializer)
|
|
{}
|
|
|
|
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
|
|
{
|
|
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5)
|
|
|| GetMaxSupportedFeatureLevel(Parameters.Platform) == ERHIFeatureLevel::ES3_1;
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
template<bool Use16Bits>
|
|
class TPageTableUpdateVS : public FPageTableUpdateVS
|
|
{
|
|
DECLARE_SHADER_TYPE(TPageTableUpdateVS,Global);
|
|
|
|
TPageTableUpdateVS() {}
|
|
|
|
public:
|
|
TPageTableUpdateVS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
|
|
: FPageTableUpdateVS(Initializer)
|
|
{}
|
|
|
|
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
|
|
{
|
|
FGlobalShader::ModifyCompilationEnvironment( Parameters, OutEnvironment );
|
|
OutEnvironment.SetDefine(TEXT("USE_16BIT"), Use16Bits);
|
|
}
|
|
};
|
|
|
|
template<EPixelFormat TargetFormat>
|
|
class TPageTableUpdatePS : public FPageTableUpdatePS
|
|
{
|
|
DECLARE_SHADER_TYPE(TPageTableUpdatePS, Global);
|
|
|
|
TPageTableUpdatePS() {}
|
|
|
|
public:
|
|
TPageTableUpdatePS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
|
|
: FPageTableUpdatePS(Initializer)
|
|
{}
|
|
|
|
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
|
|
{
|
|
FGlobalShader::ModifyCompilationEnvironment( Parameters, OutEnvironment );
|
|
OutEnvironment.SetRenderTargetOutputFormat(0u, TargetFormat);
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_SHADER_TYPE(template<>, TPageTableUpdateVS<false>, TEXT("/Engine/Private/PageTableUpdate.usf"), TEXT("PageTableUpdateVS"), SF_Vertex);
|
|
IMPLEMENT_SHADER_TYPE(template<>, TPageTableUpdateVS<true>, TEXT("/Engine/Private/PageTableUpdate.usf"), TEXT("PageTableUpdateVS"), SF_Vertex);
|
|
IMPLEMENT_SHADER_TYPE(template<>, TPageTableUpdatePS<PF_R16_UINT>, TEXT("/Engine/Private/PageTableUpdate.usf"), TEXT("PageTableUpdatePS_1"), SF_Pixel);
|
|
IMPLEMENT_SHADER_TYPE(template<>, TPageTableUpdatePS<PF_R16G16_UINT>, TEXT("/Engine/Private/PageTableUpdate.usf"), TEXT("PageTableUpdatePS_2"), SF_Pixel);
|
|
IMPLEMENT_SHADER_TYPE(template<>, TPageTableUpdatePS<PF_R16G16B16A16_UINT>, TEXT("/Engine/Private/PageTableUpdate.usf"), TEXT("PageTableUpdatePS_4"), SF_Pixel);
|
|
IMPLEMENT_SHADER_TYPE(template<>, TPageTableUpdatePS<PF_R32_UINT>, TEXT("/Engine/Private/PageTableUpdate.usf"), TEXT("PageTableUpdatePS_1"), SF_Pixel);
|
|
IMPLEMENT_SHADER_TYPE(template<>, TPageTableUpdatePS<PF_R32G32_UINT>, TEXT("/Engine/Private/PageTableUpdate.usf"), TEXT("PageTableUpdatePS_2"), SF_Pixel);
|
|
IMPLEMENT_SHADER_TYPE(template<>, TPageTableUpdatePS<PF_R32G32B32A32_UINT>, TEXT("/Engine/Private/PageTableUpdate.usf"), TEXT("PageTableUpdatePS_4"), SF_Pixel);
|
|
|
|
void FVirtualTextureSpace::QueueUpdateEntirePageTable()
|
|
{
|
|
bForceEntireUpdate = true;
|
|
}
|
|
|
|
void FVirtualTextureSpace::AllocateTextures(FRDGBuilder& GraphBuilder)
|
|
{
|
|
if (bNeedToAllocatePageTable)
|
|
{
|
|
RDG_GPU_MASK_SCOPE(GraphBuilder, FRHIGPUMask::All());
|
|
|
|
const TCHAR* TextureNames[] = { TEXT("VirtualPageTable_0"), TEXT("VirtualPageTable_1") };
|
|
static_assert(UE_ARRAY_COUNT(TextureNames) == TextureCapacity, "");
|
|
|
|
CachedPageTableWidth = Align(Allocator.GetAllocatedWidth(), VIRTUALTEXTURE_MIN_PAGETABLE_SIZE);
|
|
CachedPageTableHeight = Align(Allocator.GetAllocatedHeight(), VIRTUALTEXTURE_MIN_PAGETABLE_SIZE);
|
|
CachedNumPageTableLevels = FMath::FloorLog2(FMath::Max(CachedPageTableWidth, CachedPageTableHeight)) + 1u;
|
|
|
|
for (uint32 TextureIndex = 0u; TextureIndex < GetNumPageTableTextures(); ++TextureIndex)
|
|
{
|
|
// Page Table
|
|
FTextureEntry& TextureEntry = PageTable[TextureIndex];
|
|
const FRDGTextureDesc Desc = FRDGTextureDesc::Create2D(
|
|
FIntPoint(CachedPageTableWidth, CachedPageTableHeight),
|
|
TexturePixelFormat[TextureIndex],
|
|
FClearValueBinding::None,
|
|
TexCreate_RenderTargetable | TexCreate_ShaderResource,
|
|
CachedNumPageTableLevels);
|
|
|
|
FRDGTextureRef DstTexture = GraphBuilder.CreateTexture(Desc, TextureNames[TextureIndex]);
|
|
|
|
if (TextureEntry.RenderTarget)
|
|
{
|
|
FRDGTextureRef SrcTexture = GraphBuilder.RegisterExternalTexture(TextureEntry.RenderTarget);
|
|
const FRDGTextureDesc& SrcDesc = SrcTexture->Desc;
|
|
|
|
// Copy previously allocated page table to new texture
|
|
FRHICopyTextureInfo CopyInfo;
|
|
CopyInfo.Size.X = FMath::Min(Desc.Extent.X, SrcDesc.Extent.X);
|
|
CopyInfo.Size.Y = FMath::Min(Desc.Extent.Y, SrcDesc.Extent.Y);
|
|
CopyInfo.Size.Z = 1;
|
|
CopyInfo.NumMips = FMath::Min(Desc.NumMips, SrcDesc.NumMips);
|
|
|
|
AddCopyTexturePass(GraphBuilder, SrcTexture, DstTexture, CopyInfo);
|
|
}
|
|
|
|
TextureEntry.RenderTarget = GraphBuilder.ConvertToExternalTexture(DstTexture);
|
|
RHIUpdateTextureReference(TextureEntry.TextureReferenceRHI, TextureEntry.RenderTarget->GetRHI());
|
|
}
|
|
|
|
bNeedToAllocatePageTable = false;
|
|
}
|
|
|
|
if (bNeedToAllocatePageTableIndirection)
|
|
{
|
|
RDG_GPU_MASK_SCOPE(GraphBuilder, FRHIGPUMask::All());
|
|
|
|
if (Description.IndirectionTextureSize > 0)
|
|
{
|
|
const FRDGTextureDesc Desc = FRDGTextureDesc::Create2D(
|
|
FIntPoint(Description.IndirectionTextureSize, Description.IndirectionTextureSize),
|
|
PF_R32_UINT,
|
|
FClearValueBinding::None,
|
|
TexCreate_UAV | TexCreate_ShaderResource);
|
|
|
|
FRDGTextureRef PageTableIndirectionTexture = GraphBuilder.CreateTexture(Desc, TEXT("PageTableIndirection"));
|
|
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(PageTableIndirectionTexture), FUintVector4(ForceInitToZero));
|
|
PageTableIndirection.RenderTarget = GraphBuilder.ConvertToExternalTexture(PageTableIndirectionTexture);
|
|
RHIUpdateTextureReference(PageTableIndirection.TextureReferenceRHI, PageTableIndirection.RenderTarget->GetRHI());
|
|
}
|
|
|
|
bNeedToAllocatePageTableIndirection = false;
|
|
}
|
|
}
|
|
|
|
|
|
void FVirtualTextureSpace::ApplyUpdates(FVirtualTextureSystem* System, FRDGBuilder& GraphBuilder)
|
|
{
|
|
ON_SCOPE_EXIT
|
|
{
|
|
FinalizeTextures(GraphBuilder);
|
|
};
|
|
|
|
static TArray<FPageTableUpdate> ExpandedUpdates[VIRTUALTEXTURE_SPACE_MAXLAYERS][16];
|
|
|
|
if (bNeedToAllocatePageTable)
|
|
{
|
|
// Defer updates until next frame if page table needs to be re-allocated
|
|
// We can't update page table at this point in frame, as RHIUpdateTextureReference can't be called during RHIBegin/EndScene
|
|
return;
|
|
}
|
|
|
|
// Multi-GPU support : May be ineffecient for AFR.
|
|
RDG_GPU_MASK_SCOPE(GraphBuilder, FRHIGPUMask::All());
|
|
|
|
for (uint32 LayerIndex = 0u; LayerIndex < Description.NumPageTableLayers; ++LayerIndex)
|
|
{
|
|
FTexturePageMap& PageMap = PhysicalPageMap[LayerIndex];
|
|
if (bForceEntireUpdate || CVarVTRefreshEntirePageTable.GetValueOnRenderThread())
|
|
{
|
|
PageMap.RefreshEntirePageTable(System, ExpandedUpdates[LayerIndex]);
|
|
}
|
|
else
|
|
{
|
|
for (const FPageTableUpdate& Update : PageTableUpdates[LayerIndex])
|
|
{
|
|
if (CVarVTMaskedPageTableUpdates.GetValueOnRenderThread())
|
|
{
|
|
PageMap.ExpandPageTableUpdateMasked(System, Update, ExpandedUpdates[LayerIndex]);
|
|
}
|
|
else
|
|
{
|
|
PageMap.ExpandPageTableUpdatePainters(System, Update, ExpandedUpdates[LayerIndex]);
|
|
}
|
|
}
|
|
}
|
|
PageTableUpdates[LayerIndex].Reset();
|
|
}
|
|
bForceEntireUpdate = false;
|
|
|
|
// TODO Expand 3D updates for slices of volume texture
|
|
|
|
uint32 TotalNumUpdates = 0;
|
|
for (uint32 LayerIndex = 0u; LayerIndex < Description.NumPageTableLayers; ++LayerIndex)
|
|
{
|
|
for (uint32 Mip = 0; Mip < CachedNumPageTableLevels; Mip++)
|
|
{
|
|
TotalNumUpdates += ExpandedUpdates[LayerIndex][Mip].Num();
|
|
}
|
|
}
|
|
|
|
if (TotalNumUpdates == 0u)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (UpdateBuffer == nullptr || TotalNumUpdates * sizeof(FPageTableUpdate) > UpdateBuffer->GetSize())
|
|
{
|
|
// Resize Update Buffer
|
|
const uint32 MaxUpdates = FMath::RoundUpToPowerOfTwo(TotalNumUpdates);
|
|
uint32 NewBufferSize = MaxUpdates * sizeof(FPageTableUpdate);
|
|
if (UpdateBuffer)
|
|
{
|
|
NewBufferSize = FMath::Max(NewBufferSize, UpdateBuffer->GetSize() * 2u);
|
|
}
|
|
|
|
FRHIResourceCreateInfo CreateInfo(TEXT("FVirtualTextureSpace_UpdateBuffer"));
|
|
UpdateBuffer = RHICreateVertexBuffer(NewBufferSize, BUF_ShaderResource | BUF_Volatile, CreateInfo);
|
|
UpdateBufferSRV = RHICreateShaderResourceView(UpdateBuffer, sizeof(FPageTableUpdate), PF_R16G16B16A16_UINT);
|
|
}
|
|
|
|
// This flushes the RHI thread!
|
|
{
|
|
uint8* Buffer = (uint8*)RHILockBuffer(UpdateBuffer, 0, TotalNumUpdates * sizeof(FPageTableUpdate), RLM_WriteOnly);
|
|
for (uint32 LayerIndex = 0u; LayerIndex < Description.NumPageTableLayers; ++LayerIndex)
|
|
{
|
|
for (uint32 Mip = 0; Mip < CachedNumPageTableLevels; Mip++)
|
|
{
|
|
const uint32 NumUpdates = ExpandedUpdates[LayerIndex][Mip].Num();
|
|
if (NumUpdates)
|
|
{
|
|
size_t UploadSize = NumUpdates * sizeof(FPageTableUpdate);
|
|
FMemory::Memcpy(Buffer, ExpandedUpdates[LayerIndex][Mip].GetData(), UploadSize);
|
|
Buffer += UploadSize;
|
|
}
|
|
}
|
|
}
|
|
RHIUnlockBuffer(UpdateBuffer);
|
|
}
|
|
|
|
// Draw
|
|
RDG_EVENT_SCOPE(GraphBuilder, "PageTableUpdate");
|
|
|
|
auto ShaderMap = GetGlobalShaderMap(GetFeatureLevel());
|
|
TShaderRef<FPageTableUpdateVS> VertexShader;
|
|
if (Description.PageTableFormat == EVTPageTableFormat::UInt16)
|
|
{
|
|
VertexShader = ShaderMap->GetShader< TPageTableUpdateVS<true> >();
|
|
}
|
|
else
|
|
{
|
|
VertexShader = ShaderMap->GetShader< TPageTableUpdateVS<false> >();
|
|
}
|
|
check(VertexShader.IsValid());
|
|
|
|
uint32 FirstUpdate = 0;
|
|
for (uint32 LayerIndex = 0u; LayerIndex < Description.NumPageTableLayers; ++LayerIndex)
|
|
{
|
|
const uint32 TextureIndex = LayerIndex / LayersPerPageTableTexture;
|
|
const uint32 LayerInTexture = LayerIndex % LayersPerPageTableTexture;
|
|
|
|
FTextureEntry& PageTableEntry = PageTable[TextureIndex];
|
|
FRDGTextureRef PageTableTexture = GraphBuilder.RegisterExternalTexture(PageTableEntry.RenderTarget);
|
|
|
|
// Use color write mask to update the proper page table entry for this layer
|
|
FRHIBlendState* BlendStateRHI = nullptr;
|
|
switch (LayerInTexture)
|
|
{
|
|
case 0u: BlendStateRHI = TStaticBlendState<CW_RED>::GetRHI(); break;
|
|
case 1u: BlendStateRHI = TStaticBlendState<CW_GREEN>::GetRHI(); break;
|
|
case 2u: BlendStateRHI = TStaticBlendState<CW_BLUE>::GetRHI(); break;
|
|
case 3u: BlendStateRHI = TStaticBlendState<CW_ALPHA>::GetRHI(); break;
|
|
default: check(false); break;
|
|
}
|
|
|
|
TShaderRef<FPageTableUpdatePS> PixelShader;
|
|
switch (TexturePixelFormat[TextureIndex])
|
|
{
|
|
case PF_R16_UINT: PixelShader = ShaderMap->GetShader< TPageTableUpdatePS<PF_R16_UINT> >(); break;
|
|
case PF_R16G16_UINT: PixelShader = ShaderMap->GetShader< TPageTableUpdatePS<PF_R16G16_UINT> >(); break;
|
|
case PF_R16G16B16A16_UINT: PixelShader = ShaderMap->GetShader< TPageTableUpdatePS<PF_R16G16B16A16_UINT> >(); break;
|
|
case PF_R32_UINT: PixelShader = ShaderMap->GetShader< TPageTableUpdatePS<PF_R32_UINT> >(); break;
|
|
case PF_R32G32_UINT: PixelShader = ShaderMap->GetShader< TPageTableUpdatePS<PF_R32G32_UINT> >(); break;
|
|
case PF_R32G32B32A32_UINT: PixelShader = ShaderMap->GetShader< TPageTableUpdatePS<PF_R32G32B32A32_UINT> >(); break;
|
|
default: checkNoEntry(); break;
|
|
}
|
|
check(PixelShader.IsValid());
|
|
|
|
uint32 MipWidth = CachedPageTableWidth;
|
|
uint32 MipHeight = CachedPageTableHeight;
|
|
for (uint32 Mip = 0; Mip < CachedNumPageTableLevels; Mip++)
|
|
{
|
|
const uint32 NumUpdates = ExpandedUpdates[LayerIndex][Mip].Num();
|
|
if (NumUpdates)
|
|
{
|
|
auto* PassParameters = GraphBuilder.AllocParameters<FRenderTargetParameters>();
|
|
PassParameters->RenderTargets[0] = FRenderTargetBinding(PageTableTexture, ERenderTargetLoadAction::ELoad, Mip);
|
|
|
|
GraphBuilder.AddPass(
|
|
RDG_EVENT_NAME("PageTableUpdate (Mip: %d)", Mip),
|
|
PassParameters,
|
|
ERDGPassFlags::Raster,
|
|
[this, VertexShader, PixelShader, BlendStateRHI, FirstUpdate, NumUpdates, MipWidth, MipHeight](FRHICommandList& RHICmdList)
|
|
{
|
|
RHICmdList.SetViewport(0, 0, 0.0f, MipWidth, MipHeight, 1.0f);
|
|
|
|
FGraphicsPipelineStateInitializer GraphicsPSOInit;
|
|
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
|
|
|
|
GraphicsPSOInit.BlendState = BlendStateRHI;
|
|
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI();
|
|
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
|
|
GraphicsPSOInit.PrimitiveType = PT_TriangleList;
|
|
|
|
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GEmptyVertexDeclaration.VertexDeclarationRHI;
|
|
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
|
|
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
|
|
|
|
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);
|
|
|
|
{
|
|
FRHIVertexShader* ShaderRHI = VertexShader.GetVertexShader();
|
|
SetShaderValue(RHICmdList, ShaderRHI, VertexShader->PageTableSize, FIntPoint(CachedPageTableWidth, CachedPageTableHeight));
|
|
SetShaderValue(RHICmdList, ShaderRHI, VertexShader->FirstUpdate, FirstUpdate);
|
|
SetShaderValue(RHICmdList, ShaderRHI, VertexShader->NumUpdates, NumUpdates);
|
|
SetSRVParameter(RHICmdList, ShaderRHI, VertexShader->UpdateBuffer, UpdateBufferSRV);
|
|
}
|
|
|
|
// needs to be the same on shader side (faster on NVIDIA and AMD)
|
|
uint32 QuadsPerInstance = 8;
|
|
|
|
RHICmdList.SetStreamSource(0, NULL, 0);
|
|
RHICmdList.DrawIndexedPrimitive(GQuadIndexBuffer.IndexBufferRHI, 0, 0, 32, 0, 2 * QuadsPerInstance, FMath::DivideAndRoundUp(NumUpdates, QuadsPerInstance));
|
|
});
|
|
|
|
ExpandedUpdates[LayerIndex][Mip].Reset();
|
|
}
|
|
|
|
FirstUpdate += NumUpdates;
|
|
MipWidth = FMath::Max(MipWidth / 2u, 1u);
|
|
MipHeight = FMath::Max(MipHeight / 2u, 1u);
|
|
}
|
|
|
|
PageTableEntry.RenderTarget = GraphBuilder.ConvertToExternalTexture(PageTableTexture);
|
|
}
|
|
}
|
|
|
|
void FVirtualTextureSpace::FinalizeTextures(FRDGBuilder& GraphBuilder)
|
|
{
|
|
for (uint32 LayerIndex = 0u; LayerIndex < Description.NumPageTableLayers; ++LayerIndex)
|
|
{
|
|
const uint32 TextureIndex = LayerIndex / LayersPerPageTableTexture;
|
|
FTextureEntry& PageTableEntry = PageTable[TextureIndex];
|
|
if (PageTableEntry.RenderTarget)
|
|
{
|
|
// It's only necessary to enable external access mode on textures modified by RDG this frame.
|
|
if (FRDGTexture* Texture = GraphBuilder.FindExternalTexture(PageTableEntry.RenderTarget))
|
|
{
|
|
GraphBuilder.UseExternalAccessMode(Texture, ERHIAccess::SRVMask);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FVirtualTextureSpace::DumpToConsole(bool verbose)
|
|
{
|
|
UE_LOG(LogConsoleResponse, Display, TEXT("-= Space ID %i =-"), ID);
|
|
Allocator.DumpToConsole(verbose);
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
void FVirtualTextureSpace::SaveAllocatorDebugImage() const
|
|
{
|
|
const FString ImageName = FString::Printf(TEXT("Space%dAllocator.png"), ID);
|
|
Allocator.SaveDebugImage(*ImageName);
|
|
}
|
|
#endif |