You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
385 lines
13 KiB
C++
385 lines
13 KiB
C++
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "VirtualTextureSpace.h"
|
|
|
|
#include "VirtualTextureSystem.h"
|
|
#include "SpriteIndexBuffer.h"
|
|
#include "SceneFilterRendering.h"
|
|
#include "RenderTargetPool.h"
|
|
#include "GlobalShader.h"
|
|
#include "PipelineStateCache.h"
|
|
#include "HAL/IConsoleManager.h"
|
|
#include "SceneUtils.h"
|
|
#include "VisualizeTexture.h"
|
|
#include "CommonRenderResources.h"
|
|
|
|
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
|
|
);
|
|
|
|
|
|
FVirtualTextureSpace::FVirtualTextureSpace(const FVirtualTextureSpaceDesc& desc)
|
|
: ID( 0xff )
|
|
, Dimensions( desc.Dimensions )
|
|
, Allocator( desc.Size, desc.Dimensions)
|
|
, bForceEntireUpdate(false)
|
|
{
|
|
Pool = new FTexturePagePool(desc.Poolsize * desc.Poolsize, desc.Dimensions);
|
|
PhysicalTextureSize = FIntPoint(desc.Poolsize * desc.PhysicalTileSize, desc.Poolsize * desc.PhysicalTileSize);
|
|
FMemory::Memcpy(PhysicalTextureFormats, desc.PhysicalTextureFormats, sizeof(EPixelFormat) * VIRTUALTEXTURESPACE_MAXLAYERS);
|
|
|
|
PageTableSize = desc.Size;
|
|
PageTableLevels = FMath::FloorLog2( PageTableSize ) + 1;
|
|
PageTableFormat = desc.PageTableFormat;
|
|
|
|
GetVirtualTextureSystem()->RegisterSpace( this );
|
|
}
|
|
|
|
FVirtualTextureSpace::~FVirtualTextureSpace()
|
|
{
|
|
delete Pool;
|
|
Pool = nullptr;
|
|
|
|
GetVirtualTextureSystem()->UnregisterSpace( this );
|
|
}
|
|
|
|
void FVirtualTextureSpace::InitDynamicRHI()
|
|
{
|
|
FRHICommandListImmediate& RHICmdList = FRHICommandListExecutor::GetImmediateCommandList();
|
|
|
|
const TCHAR* DebugNames[VIRTUALTEXTURESPACE_MAXLAYERS] =
|
|
{
|
|
TEXT("PhysicalTexture_0"),
|
|
TEXT("PhysicalTexture_1"),
|
|
TEXT("PhysicalTexture_2"),
|
|
TEXT("PhysicalTexture_3"),
|
|
};
|
|
|
|
for (uint32 i = 0; i < VIRTUALTEXTURESPACE_MAXLAYERS; ++i)
|
|
{
|
|
if (PhysicalTextureFormats[i] == PF_Unknown)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
FPooledRenderTargetDesc Desc = FPooledRenderTargetDesc::Create2DDesc(PhysicalTextureSize, PhysicalTextureFormats[i], FClearValueBinding::None,
|
|
TexCreate_None, TexCreate_ShaderResource, false);
|
|
|
|
GRenderTargetPool.FindFreeElement(RHICmdList, Desc, PhysicalTextures[i], DebugNames[i]);
|
|
}
|
|
|
|
|
|
{
|
|
// Page Table
|
|
FPooledRenderTargetDesc Desc( FPooledRenderTargetDesc::Create2DDesc( FIntPoint( PageTableSize, PageTableSize ), PageTableFormat, FClearValueBinding::None, TexCreate_None, TexCreate_RenderTargetable | TexCreate_ShaderResource, false, PageTableLevels ) );
|
|
GRenderTargetPool.FindFreeElement( RHICmdList, Desc, PageTable, TEXT("PageTable") );
|
|
}
|
|
|
|
{
|
|
// Worst case scenario of max mip page update when all other pages are perfectly sparse at mip 0.
|
|
const uint32 MaxSparseRegions = Pool->GetSize();
|
|
const uint32 SparseRegionSize = PageTableSize / (uint32)FMath::Pow( (float)MaxSparseRegions, 1.0f / Dimensions );
|
|
const uint32 PerRegionMaxExpansion = ( (1 << Dimensions) - 1 ) * FMath::FloorLog2( SparseRegionSize );
|
|
const uint32 MaxExpansionMip0 = PerRegionMaxExpansion * MaxSparseRegions;
|
|
const uint32 Mip0Updates = 64;
|
|
const uint32 MaxUpdates = MaxExpansionMip0 + (PageTableLevels - 1) + Mip0Updates;
|
|
|
|
// Update Buffer
|
|
FRHIResourceCreateInfo CreateInfo;
|
|
UpdateBuffer = RHICreateStructuredBuffer( sizeof( FPageTableUpdate ), MaxUpdates * sizeof( FPageTableUpdate ), BUF_ShaderResource | BUF_Volatile, CreateInfo );
|
|
UpdateBufferSRV = RHICreateShaderResourceView( UpdateBuffer );
|
|
}
|
|
}
|
|
|
|
void FVirtualTextureSpace::ReleaseDynamicRHI()
|
|
{
|
|
for (uint32 i = 0; i < VIRTUALTEXTURESPACE_MAXLAYERS; ++i)
|
|
{
|
|
GRenderTargetPool.FreeUnusedResource(PhysicalTextures[i]);
|
|
}
|
|
|
|
GRenderTargetPool.FreeUnusedResource( PageTable );
|
|
|
|
UpdateBuffer.SafeRelease();
|
|
UpdateBufferSRV.SafeRelease();
|
|
}
|
|
|
|
void FVirtualTextureSpace::QueueUpdate( uint8 vLogSize, uint64 vAddress, uint8 vLevel, uint16 pAddress )
|
|
{
|
|
FPageUpdate Update;
|
|
Update.vAddress = vAddress;
|
|
Update.pAddress = pAddress;
|
|
Update.vLevel = vLevel;
|
|
Update.vLogSize = vLogSize;
|
|
Update.Check( Dimensions );
|
|
|
|
PageTableUpdates.Add( Update );
|
|
}
|
|
|
|
|
|
TGlobalResource< FSpriteIndexBuffer<8> > GQuadIndexBuffer;
|
|
|
|
class FPageTableUpdateVS : public FGlobalShader
|
|
{
|
|
DECLARE_SHADER_TYPE(FPageTableUpdateVS,Global);
|
|
|
|
FPageTableUpdateVS() {}
|
|
|
|
public:
|
|
FShaderParameter PageTableSize;
|
|
FShaderParameter FirstUpdate;
|
|
FShaderParameter NumUpdates;
|
|
FShaderResourceParameter UpdateBuffer;
|
|
|
|
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) && !IsHlslccShaderPlatform(Parameters.Platform);
|
|
}
|
|
|
|
virtual bool Serialize( FArchive& Ar ) override
|
|
{
|
|
bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
|
|
Ar << PageTableSize;
|
|
Ar << FirstUpdate;
|
|
Ar << NumUpdates;
|
|
Ar << UpdateBuffer;
|
|
return bShaderHasOutdatedParameters;
|
|
}
|
|
};
|
|
|
|
class FPageTableUpdatePS : public FGlobalShader
|
|
{
|
|
DECLARE_SHADER_TYPE(FPageTableUpdatePS, Global);
|
|
|
|
FPageTableUpdatePS() {}
|
|
|
|
public:
|
|
FPageTableUpdatePS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
|
|
: FGlobalShader(Initializer)
|
|
{}
|
|
|
|
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
|
|
{
|
|
return IsFeatureLevelSupported( Parameters.Platform, ERHIFeatureLevel::SM5 ) && !IsHlslccShaderPlatform(Parameters.Platform);
|
|
}
|
|
};
|
|
|
|
template< uint32 Format >
|
|
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("PAGE_TABLE_FORMAT"), Format );
|
|
}
|
|
};
|
|
|
|
template< uint32 Format >
|
|
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.SetDefine( TEXT("PAGE_TABLE_FORMAT"), Format );
|
|
OutEnvironment.SetRenderTargetOutputFormat( 0, Format == 0 ? PF_R16_UINT : PF_R8G8B8A8 );
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_SHADER_TYPE( template<>, TPageTableUpdateVS<0>, TEXT("/Engine/Private/PageTableUpdate.usf"), TEXT("PageTableUpdateVS"), SF_Vertex );
|
|
IMPLEMENT_SHADER_TYPE( template<>, TPageTableUpdateVS<1>, TEXT("/Engine/Private/PageTableUpdate.usf"), TEXT("PageTableUpdateVS"), SF_Vertex );
|
|
IMPLEMENT_SHADER_TYPE( template<>, TPageTableUpdatePS<0>, TEXT("/Engine/Private/PageTableUpdate.usf"), TEXT("PageTableUpdatePS"), SF_Pixel );
|
|
IMPLEMENT_SHADER_TYPE( template<>, TPageTableUpdatePS<1>, TEXT("/Engine/Private/PageTableUpdate.usf"), TEXT("PageTableUpdatePS"), SF_Pixel );
|
|
|
|
void FVirtualTextureSpace::QueueUpdateEntirePageTable()
|
|
{
|
|
bForceEntireUpdate = true;
|
|
}
|
|
|
|
void FVirtualTextureSpace::ApplyUpdates( FRHICommandList& RHICmdList )
|
|
{
|
|
static TArray< FPageTableUpdate > ExpandedUpdates[16];
|
|
|
|
if( bForceEntireUpdate || CVarVTRefreshEntirePageTable.GetValueOnRenderThread() )
|
|
{
|
|
Pool->RefreshEntirePageTable( ID, ExpandedUpdates );
|
|
bForceEntireUpdate = false;
|
|
}
|
|
else
|
|
{
|
|
if( PageTableUpdates.Num() == 0 )
|
|
{
|
|
GVisualizeTexture.SetCheckPoint( RHICmdList, PageTable );
|
|
return;
|
|
}
|
|
|
|
for( auto& Update : PageTableUpdates )
|
|
{
|
|
if( CVarVTMaskedPageTableUpdates.GetValueOnRenderThread() )
|
|
{
|
|
Pool->ExpandPageTableUpdateMasked( ID, Update, ExpandedUpdates );
|
|
}
|
|
else
|
|
{
|
|
Pool->ExpandPageTableUpdatePainters( ID, Update, ExpandedUpdates );
|
|
}
|
|
}
|
|
}
|
|
|
|
PageTableUpdates.Reset();
|
|
|
|
// TODO Expand 3D updates for slices of volume texture
|
|
|
|
uint32 TotalNumUpdates = 0;
|
|
for( uint32 Mip = 0; Mip < PageTableLevels; Mip++ )
|
|
{
|
|
TotalNumUpdates += ExpandedUpdates[ Mip ].Num();
|
|
}
|
|
|
|
if( TotalNumUpdates * sizeof( FPageTableUpdate ) > UpdateBuffer->GetSize() )
|
|
{
|
|
// Resize Update Buffer
|
|
uint32 MaxUpdates = FMath::RoundUpToPowerOfTwo( TotalNumUpdates );
|
|
|
|
FRHIResourceCreateInfo CreateInfo;
|
|
UpdateBuffer = RHICreateStructuredBuffer( sizeof( FPageUpdate ), MaxUpdates * sizeof( FPageTableUpdate ), BUF_ShaderResource | BUF_Volatile, CreateInfo );
|
|
UpdateBufferSRV = RHICreateShaderResourceView( UpdateBuffer );
|
|
}
|
|
|
|
// This flushes the RHI thread!
|
|
uint8* Buffer = (uint8*)RHILockStructuredBuffer( UpdateBuffer, 0, TotalNumUpdates * sizeof( FPageTableUpdate ), RLM_WriteOnly );
|
|
|
|
for( uint32 Mip = 0; Mip < PageTableLevels; Mip++ )
|
|
{
|
|
uint32 NumUpdates = ExpandedUpdates[ Mip ].Num();
|
|
if( NumUpdates )
|
|
{
|
|
size_t UploadSize = NumUpdates * sizeof( FPageTableUpdate );
|
|
FMemory::Memcpy( Buffer, ExpandedUpdates[ Mip ].GetData(), UploadSize );
|
|
Buffer += UploadSize;
|
|
}
|
|
}
|
|
|
|
RHIUnlockStructuredBuffer( UpdateBuffer );
|
|
|
|
// Draw
|
|
SCOPED_DRAW_EVENT( RHICmdList, PageTableUpdate );
|
|
|
|
auto ShaderMap = GetGlobalShaderMap( GetFeatureLevel() );
|
|
|
|
FSceneRenderTargetItem& PageTableTarget = PageTable->GetRenderTargetItem();
|
|
|
|
uint32 FirstUpdate = 0;
|
|
uint32 MipSize = PageTableSize;
|
|
for( uint32 Mip = 0; Mip < PageTableLevels; Mip++ )
|
|
{
|
|
uint32 NumUpdates = ExpandedUpdates[ Mip ].Num();
|
|
if( NumUpdates )
|
|
{
|
|
// We will resolve this target once after all mips are updated.
|
|
FRHIRenderPassInfo RPInfo(PageTableTarget.TargetableTexture, ERenderTargetActions::Load_Store);
|
|
RPInfo.ColorRenderTargets[0].MipIndex = Mip;
|
|
RHICmdList.BeginRenderPass(RPInfo, TEXT("VirtualTextureUpdate"));
|
|
{
|
|
RHICmdList.SetViewport(0, 0, 0.0f, MipSize, MipSize, 1.0f);
|
|
|
|
FGraphicsPipelineStateInitializer GraphicsPSOInit;
|
|
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
|
|
|
|
GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI();
|
|
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI();
|
|
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
|
|
GraphicsPSOInit.PrimitiveType = PT_TriangleList;
|
|
|
|
FPageTableUpdateVS* VertexShader = nullptr;
|
|
FPageTableUpdatePS* PixelShader = nullptr;
|
|
|
|
switch (PageTableFormat)
|
|
{
|
|
case PF_R16_UINT:
|
|
{
|
|
VertexShader = *TShaderMapRef< TPageTableUpdateVS<0> >(ShaderMap);
|
|
PixelShader = *TShaderMapRef< TPageTableUpdatePS<0> >(ShaderMap);
|
|
}
|
|
break;
|
|
case PF_R8G8B8A8:
|
|
{
|
|
VertexShader = *TShaderMapRef< TPageTableUpdateVS<1> >(ShaderMap);
|
|
PixelShader = *TShaderMapRef< TPageTableUpdatePS<1> >(ShaderMap);
|
|
}
|
|
break;
|
|
default:
|
|
check(0);
|
|
}
|
|
checkSlow(VertexShader && PixelShader);
|
|
|
|
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GEmptyVertexDeclaration.VertexDeclarationRHI;
|
|
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = GETSAFERHISHADER_VERTEX(VertexShader);
|
|
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = GETSAFERHISHADER_PIXEL(PixelShader);
|
|
|
|
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit);
|
|
|
|
{
|
|
const FVertexShaderRHIParamRef ShaderRHI = VertexShader->GetVertexShader();
|
|
|
|
SetShaderValue(RHICmdList, ShaderRHI, VertexShader->PageTableSize, PageTableSize);
|
|
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));
|
|
}
|
|
RHICmdList.EndRenderPass();
|
|
|
|
ExpandedUpdates[ Mip ].Reset();
|
|
}
|
|
|
|
FirstUpdate += NumUpdates;
|
|
MipSize >>= 1;
|
|
}
|
|
|
|
RHICmdList.CopyToResolveTarget( PageTableTarget.TargetableTexture, PageTableTarget.ShaderResourceTexture, FResolveParams() );
|
|
|
|
GVisualizeTexture.SetCheckPoint( RHICmdList, PageTable );
|
|
} |