Files
UnrealEngineUWP/Engine/Source/Runtime/Renderer/Private/SystemTextures.cpp
Charles deRousiers 535d1d95c1 Add render graph support for default textures and default buffers with custom format and custom default value.
#rb zach.bethel, sebastien.hillaire
#jira none

[CL 16183014 by Charles deRousiers in ue5-main branch]
2021-05-03 06:44:33 -04:00

1155 lines
55 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
SystemTextures.cpp: System textures implementation.
=============================================================================*/
#include "SystemTextures.h"
#include "Math/RandomStream.h"
#include "Math/Sobol.h"
#include "RenderTargetPool.h"
#include "ClearQuad.h"
#include "LTC.h"
/*-----------------------------------------------------------------------------
SystemTextures
-----------------------------------------------------------------------------*/
RDG_REGISTER_BLACKBOARD_STRUCT(FRDGSystemTextures);
const FRDGSystemTextures& FRDGSystemTextures::Create(FRDGBuilder& GraphBuilder)
{
const auto Register = [&](const TRefCountPtr<IPooledRenderTarget>& RenderTarget)
{
return TryRegisterExternalTexture(GraphBuilder, RenderTarget, ERenderTargetTexture::ShaderResource, ERDGTextureFlags::ReadOnly);
};
auto& SystemTextures = GraphBuilder.Blackboard.Create<FRDGSystemTextures>();
SystemTextures.White = Register(GSystemTextures.WhiteDummy);
SystemTextures.Black = Register(GSystemTextures.BlackDummy);
SystemTextures.BlackAlphaOne = Register(GSystemTextures.BlackAlphaOneDummy);
SystemTextures.MaxFP16Depth = Register(GSystemTextures.MaxFP16Depth);
SystemTextures.DepthDummy = Register(GSystemTextures.DepthDummy);
SystemTextures.StencilDummy = Register(GSystemTextures.StencilDummy);
SystemTextures.Green = Register(GSystemTextures.GreenDummy);
SystemTextures.DefaultNormal8Bit = Register(GSystemTextures.DefaultNormal8Bit);
SystemTextures.MidGrey = Register(GSystemTextures.MidGreyDummy);
SystemTextures.VolumetricBlack = Register(GSystemTextures.VolumetricBlackDummy);
SystemTextures.StencilDummySRV = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::CreateWithPixelFormat(SystemTextures.DepthDummy, PF_X24_G8));
return SystemTextures;
}
const FRDGSystemTextures& FRDGSystemTextures::Get(FRDGBuilder& GraphBuilder)
{
const FRDGSystemTextures* SystemTextures = GraphBuilder.Blackboard.Get<FRDGSystemTextures>();
checkf(SystemTextures, TEXT("FRDGSystemTextures were not initialized. Call FRDGSystemTextures::Create() first."));
return *SystemTextures;
}
bool FRDGSystemTextures::IsValid(FRDGBuilder& GraphBuilder)
{
return GraphBuilder.Blackboard.Get<FRDGSystemTextures>() != nullptr;
}
/** The global render targets used for scene rendering. */
TGlobalResource<FSystemTextures> GSystemTextures;
void FSystemTextures::InitializeTextures(FRHICommandListImmediate& RHICmdList, const ERHIFeatureLevel::Type InFeatureLevel)
{
// When we render to system textures it should occur on all GPUs since this only
// happens once on startup (or when the feature level changes).
SCOPED_GPU_MASK(RHICmdList, FRHIGPUMask::All());
// if this is the first call initialize everything
if (FeatureLevelInitializedTo == ERHIFeatureLevel::Num)
{
InitializeCommonTextures(RHICmdList);
InitializeFeatureLevelDependentTextures(RHICmdList, InFeatureLevel);
}
// otherwise, if we request a higher feature level, we might need to initialize those textures that depend on the feature level
else if (InFeatureLevel > FeatureLevelInitializedTo)
{
InitializeFeatureLevelDependentTextures(RHICmdList, InFeatureLevel);
}
// there's no needed setup for those feature levels lower or identical to the current one
}
void FSystemTextures::InitializeCommonTextures(FRHICommandListImmediate& RHICmdList)
{
// First initialize textures that are common to all feature levels. This is always done the first time we come into this function, as doesn't care about the
// requested feature level
// Create a WhiteDummy texture
{
FPooledRenderTargetDesc Desc(FPooledRenderTargetDesc::Create2DDesc(FIntPoint(1, 1), PF_B8G8R8A8, FClearValueBinding::White, TexCreate_HideInVisualizeTexture, TexCreate_RenderTargetable | TexCreate_NoFastClear | TexCreate_ShaderResource, false));
Desc.AutoWritable = false;
GRenderTargetPool.FindFreeElement(RHICmdList, Desc, WhiteDummy, TEXT("WhiteDummy"), ERenderTargetTransience::NonTransient);
RHICmdList.Transition(FRHITransitionInfo(WhiteDummy->GetRenderTargetItem().TargetableTexture, ERHIAccess::SRVMask, ERHIAccess::RTV));
FRHIRenderPassInfo RPInfo(WhiteDummy->GetRenderTargetItem().TargetableTexture, ERenderTargetActions::Clear_Store);
RHICmdList.BeginRenderPass(RPInfo, TEXT("WhiteDummy"));
RHICmdList.EndRenderPass();
RHICmdList.CopyToResolveTarget(WhiteDummy->GetRenderTargetItem().TargetableTexture, WhiteDummy->GetRenderTargetItem().ShaderResourceTexture, FResolveParams());
WhiteDummySRV = RHICreateShaderResourceView((FRHITexture2D*)WhiteDummy->GetRenderTargetItem().ShaderResourceTexture.GetReference(), 0);
}
// Create a BlackDummy texture
{
FPooledRenderTargetDesc Desc(FPooledRenderTargetDesc::Create2DDesc(FIntPoint(1, 1), PF_B8G8R8A8, FClearValueBinding::Transparent, TexCreate_HideInVisualizeTexture, TexCreate_RenderTargetable | TexCreate_NoFastClear | TexCreate_ShaderResource, false));
Desc.AutoWritable = false;
GRenderTargetPool.FindFreeElement(RHICmdList, Desc, BlackDummy, TEXT("BlackDummy"), ERenderTargetTransience::NonTransient);
RHICmdList.Transition(FRHITransitionInfo(BlackDummy->GetRenderTargetItem().TargetableTexture, ERHIAccess::SRVMask, ERHIAccess::RTV));
FRHIRenderPassInfo RPInfo(BlackDummy->GetRenderTargetItem().TargetableTexture, ERenderTargetActions::Clear_Store);
RHICmdList.BeginRenderPass(RPInfo, TEXT("BlackDummy"));
RHICmdList.EndRenderPass();
RHICmdList.CopyToResolveTarget(BlackDummy->GetRenderTargetItem().TargetableTexture, BlackDummy->GetRenderTargetItem().ShaderResourceTexture, FResolveParams());
}
// Create a texture that is a single UInt32 value set to 0
{
FPooledRenderTargetDesc Desc(FPooledRenderTargetDesc::Create2DDesc(FIntPoint(1,1), PF_R32_UINT, FClearValueBinding::Transparent, TexCreate_HideInVisualizeTexture, TexCreate_RenderTargetable | TexCreate_NoFastClear | TexCreate_ShaderResource, false));
Desc.AutoWritable = false;
GRenderTargetPool.FindFreeElement(RHICmdList, Desc, ZeroUIntDummy, TEXT("ZeroUIntDummy"), ERenderTargetTransience::NonTransient);
RHICmdList.Transition(FRHITransitionInfo(ZeroUIntDummy->GetRenderTargetItem().TargetableTexture, ERHIAccess::SRVMask, ERHIAccess::RTV));
FRHIRenderPassInfo RPInfo(ZeroUIntDummy->GetRenderTargetItem().TargetableTexture, ERenderTargetActions::Clear_Store);
RHICmdList.BeginRenderPass(RPInfo, TEXT("ClearZeroUIntDummy"));
RHICmdList.EndRenderPass();
RHICmdList.CopyToResolveTarget(ZeroUIntDummy->GetRenderTargetItem().TargetableTexture, ZeroUIntDummy->GetRenderTargetItem().ShaderResourceTexture, FResolveParams());
}
// Create a texture that is a single 4xUInt16 (UShort) value set to 0
{
FPooledRenderTargetDesc Desc(FPooledRenderTargetDesc::Create2DDesc(FIntPoint(1, 1), PF_R16G16B16A16_UINT, FClearValueBinding::Transparent, TexCreate_HideInVisualizeTexture, TexCreate_RenderTargetable | TexCreate_NoFastClear | TexCreate_ShaderResource, false));
Desc.AutoWritable = false;
GRenderTargetPool.FindFreeElement(RHICmdList, Desc, ZeroUShort4Dummy, TEXT("ZeroUShort4Dummy"), ERenderTargetTransience::NonTransient);
RHICmdList.Transition(FRHITransitionInfo(ZeroUShort4Dummy->GetRenderTargetItem().TargetableTexture, ERHIAccess::SRVMask, ERHIAccess::RTV));
FRHIRenderPassInfo RPInfo(ZeroUShort4Dummy->GetRenderTargetItem().TargetableTexture, ERenderTargetActions::Clear_Store);
RHICmdList.BeginRenderPass(RPInfo, TEXT("ClearZeroUShort4Dummy"));
RHICmdList.EndRenderPass();
RHICmdList.CopyToResolveTarget(ZeroUShort4Dummy->GetRenderTargetItem().TargetableTexture, ZeroUShort4Dummy->GetRenderTargetItem().ShaderResourceTexture, FResolveParams());
}
// Create a BlackAlphaOneDummy texture
{
FPooledRenderTargetDesc Desc(FPooledRenderTargetDesc::Create2DDesc(FIntPoint(1, 1), PF_B8G8R8A8, FClearValueBinding::Black, TexCreate_HideInVisualizeTexture, TexCreate_RenderTargetable | TexCreate_NoFastClear | TexCreate_ShaderResource, false));
Desc.AutoWritable = false;
GRenderTargetPool.FindFreeElement(RHICmdList, Desc, BlackAlphaOneDummy, TEXT("BlackAlphaOneDummy"), ERenderTargetTransience::NonTransient);
RHICmdList.Transition(FRHITransitionInfo(BlackAlphaOneDummy->GetRenderTargetItem().TargetableTexture, ERHIAccess::SRVMask, ERHIAccess::RTV));
FRHIRenderPassInfo RPInfo(BlackAlphaOneDummy->GetRenderTargetItem().TargetableTexture, ERenderTargetActions::Clear_Store);
RHICmdList.BeginRenderPass(RPInfo, TEXT("BlackAlphaOneDummy"));
RHICmdList.EndRenderPass();
RHICmdList.CopyToResolveTarget(BlackAlphaOneDummy->GetRenderTargetItem().TargetableTexture, BlackAlphaOneDummy->GetRenderTargetItem().ShaderResourceTexture, FResolveParams());
}
// Create a GreenDummy texture
{
FPooledRenderTargetDesc Desc(FPooledRenderTargetDesc::Create2DDesc(FIntPoint(1, 1), PF_B8G8R8A8, FClearValueBinding::Green, TexCreate_HideInVisualizeTexture, TexCreate_RenderTargetable | TexCreate_NoFastClear | TexCreate_ShaderResource, false));
Desc.AutoWritable = false;
GRenderTargetPool.FindFreeElement(RHICmdList, Desc, GreenDummy, TEXT("GreenDummy"), ERenderTargetTransience::NonTransient);
RHICmdList.Transition(FRHITransitionInfo(GreenDummy->GetRenderTargetItem().TargetableTexture, ERHIAccess::SRVMask, ERHIAccess::RTV));
FRHIRenderPassInfo RPInfo(GreenDummy->GetRenderTargetItem().TargetableTexture, ERenderTargetActions::Clear_Store);
RHICmdList.BeginRenderPass(RPInfo, TEXT("GreenDummy"));
RHICmdList.EndRenderPass();
RHICmdList.CopyToResolveTarget(GreenDummy->GetRenderTargetItem().TargetableTexture, GreenDummy->GetRenderTargetItem().ShaderResourceTexture, FResolveParams());
}
// Create a DefaultNormal8Bit texture
{
FPooledRenderTargetDesc Desc(FPooledRenderTargetDesc::Create2DDesc(FIntPoint(1, 1), PF_B8G8R8A8, FClearValueBinding::DefaultNormal8Bit, TexCreate_HideInVisualizeTexture, TexCreate_RenderTargetable | TexCreate_NoFastClear | TexCreate_ShaderResource, false));
Desc.AutoWritable = false;
GRenderTargetPool.FindFreeElement(RHICmdList, Desc, DefaultNormal8Bit, TEXT("DefaultNormal8Bit"), ERenderTargetTransience::NonTransient);
RHICmdList.Transition(FRHITransitionInfo(DefaultNormal8Bit->GetRenderTargetItem().TargetableTexture, ERHIAccess::SRVMask, ERHIAccess::RTV));
FRHIRenderPassInfo RPInfo(DefaultNormal8Bit->GetRenderTargetItem().TargetableTexture, ERenderTargetActions::Clear_Store);
RHICmdList.BeginRenderPass(RPInfo, TEXT("DefaultNormal8Bit"));
RHICmdList.EndRenderPass();
RHICmdList.CopyToResolveTarget(DefaultNormal8Bit->GetRenderTargetItem().TargetableTexture, DefaultNormal8Bit->GetRenderTargetItem().ShaderResourceTexture, FResolveParams());
}
// Create the PerlinNoiseGradient texture
{
FPooledRenderTargetDesc Desc(FPooledRenderTargetDesc::Create2DDesc(FIntPoint(128, 128), PF_B8G8R8A8, FClearValueBinding::None, TexCreate_HideInVisualizeTexture, TexCreate_None | TexCreate_NoFastClear | TexCreate_ShaderResource, false));
Desc.AutoWritable = false;
GRenderTargetPool.FindFreeElement(RHICmdList, Desc, PerlinNoiseGradient, TEXT("PerlinNoiseGradient"), ERenderTargetTransience::NonTransient);
// Write the contents of the texture.
uint32 DestStride;
uint8* DestBuffer = (uint8*)RHICmdList.LockTexture2D((FTexture2DRHIRef&)PerlinNoiseGradient->GetRenderTargetItem().ShaderResourceTexture, 0, RLM_WriteOnly, DestStride, false);
// seed the pseudo random stream with a good value
FRandomStream RandomStream(12345);
// Values represent float3 values in the -1..1 range.
// The vectors are the edge mid point of a cube from -1 .. 1
static uint32 gradtable[] =
{
0x88ffff, 0xff88ff, 0xffff88,
0x88ff00, 0xff8800, 0xff0088,
0x8800ff, 0x0088ff, 0x00ff88,
0x880000, 0x008800, 0x000088,
};
for (int32 y = 0; y < Desc.Extent.Y; ++y)
{
for (int32 x = 0; x < Desc.Extent.X; ++x)
{
uint32* Dest = (uint32*)(DestBuffer + x * sizeof(uint32) + y * DestStride);
// pick a random direction (hacky way to overcome the quality issues FRandomStream has)
*Dest = gradtable[(uint32)(RandomStream.GetFraction() * 11.9999999f)];
}
}
RHICmdList.UnlockTexture2D((FTexture2DRHIRef&)PerlinNoiseGradient->GetRenderTargetItem().ShaderResourceTexture, 0, false);
}
if (GPixelFormats[PF_FloatRGBA].Supported)
{
FPooledRenderTargetDesc Desc(FPooledRenderTargetDesc::Create2DDesc(FIntPoint(1, 1), PF_FloatRGBA, FClearValueBinding(FLinearColor(65500.0f, 65500.0f, 65500.0f, 65500.0f)), TexCreate_HideInVisualizeTexture, TexCreate_RenderTargetable | TexCreate_NoFastClear | TexCreate_ShaderResource, false));
Desc.AutoWritable = false;
GRenderTargetPool.FindFreeElement(RHICmdList, Desc, MaxFP16Depth, TEXT("MaxFP16Depth"), ERenderTargetTransience::NonTransient);
RHICmdList.Transition(FRHITransitionInfo(MaxFP16Depth->GetRenderTargetItem().TargetableTexture, ERHIAccess::SRVMask, ERHIAccess::RTV));
FRHIRenderPassInfo RPInfo(MaxFP16Depth->GetRenderTargetItem().TargetableTexture, ERenderTargetActions::Clear_Store);
RHICmdList.BeginRenderPass(RPInfo, TEXT("MaxFP16Depth"));
RHICmdList.EndRenderPass();
RHICmdList.CopyToResolveTarget(MaxFP16Depth->GetRenderTargetItem().TargetableTexture, MaxFP16Depth->GetRenderTargetItem().ShaderResourceTexture, FResolveParams());
}
// Create dummy 1x1 depth texture
{
FPooledRenderTargetDesc Desc(FPooledRenderTargetDesc::Create2DDesc(FIntPoint(1, 1), PF_DepthStencil, FClearValueBinding::DepthFar, TexCreate_None, TexCreate_DepthStencilTargetable, false));
Desc.AutoWritable = false;
GRenderTargetPool.FindFreeElement(RHICmdList, Desc, DepthDummy, TEXT("DepthDummy"), ERenderTargetTransience::NonTransient);
RHICmdList.Transition(FRHITransitionInfo(DepthDummy->GetRenderTargetItem().TargetableTexture, ERHIAccess::SRVMask, ERHIAccess::DSVWrite));
FRHIRenderPassInfo RPInfo(DepthDummy->GetRenderTargetItem().TargetableTexture, EDepthStencilTargetActions::ClearDepthStencil_StoreDepthStencil, nullptr, FExclusiveDepthStencil::DepthWrite_StencilWrite);
RHICmdList.BeginRenderPass(RPInfo, TEXT("DepthDummy"));
RHICmdList.EndRenderPass();
RHICmdList.CopyToResolveTarget(DepthDummy->GetRenderTargetItem().TargetableTexture, DepthDummy->GetRenderTargetItem().ShaderResourceTexture, FResolveParams());
}
// Create a dummy stencil SRV.
{
FPooledRenderTargetDesc Desc(FPooledRenderTargetDesc::Create2DDesc(FIntPoint(1, 1), PF_R8G8B8A8_UINT, FClearValueBinding::White, TexCreate_HideInVisualizeTexture, TexCreate_RenderTargetable | TexCreate_NoFastClear, false));
Desc.AutoWritable = false;
GRenderTargetPool.FindFreeElement(RHICmdList, Desc, StencilDummy, TEXT("StencilDummy"), ERenderTargetTransience::NonTransient);
RHICmdList.Transition(FRHITransitionInfo(StencilDummy->GetRenderTargetItem().TargetableTexture, ERHIAccess::SRVMask, ERHIAccess::RTV));
FRHIRenderPassInfo RPInfo(StencilDummy->GetRenderTargetItem().TargetableTexture, ERenderTargetActions::Clear_Store);
RHICmdList.BeginRenderPass(RPInfo, TEXT("StencilDummy"));
RHICmdList.EndRenderPass();
RHICmdList.CopyToResolveTarget(StencilDummy->GetRenderTargetItem().TargetableTexture, StencilDummy->GetRenderTargetItem().ShaderResourceTexture, FResolveParams());
StencilDummySRV = RHICreateShaderResourceView((FRHITexture2D*)StencilDummy->GetRenderTargetItem().ShaderResourceTexture.GetReference(), 0);
}
if (GPixelFormats[PF_FloatRGBA].Supported)
{
// PF_FloatRGBA to encode exactly the 0.5.
FPooledRenderTargetDesc Desc(FPooledRenderTargetDesc::Create2DDesc(FIntPoint(1, 1), PF_FloatRGBA, FClearValueBinding(FLinearColor(0.5f, 0.5f, 0.5f, 0.5f)), TexCreate_HideInVisualizeTexture, TexCreate_RenderTargetable | TexCreate_NoFastClear | TexCreate_ShaderResource, false));
Desc.AutoWritable = false;
GRenderTargetPool.FindFreeElement(RHICmdList, Desc, MidGreyDummy, TEXT("MidGreyDummy"), ERenderTargetTransience::NonTransient);
RHICmdList.Transition(FRHITransitionInfo(MidGreyDummy->GetRenderTargetItem().TargetableTexture, ERHIAccess::SRVMask, ERHIAccess::RTV));
FRHIRenderPassInfo RPInfo(MidGreyDummy->GetRenderTargetItem().TargetableTexture, ERenderTargetActions::Clear_Store);
RHICmdList.BeginRenderPass(RPInfo, TEXT("MidGreyDummy"));
RHICmdList.EndRenderPass();
RHICmdList.CopyToResolveTarget(MidGreyDummy->GetRenderTargetItem().TargetableTexture, MidGreyDummy->GetRenderTargetItem().ShaderResourceTexture, FResolveParams());
}
}
void FSystemTextures::InitializeFeatureLevelDependentTextures(FRHICommandListImmediate& RHICmdList, const ERHIFeatureLevel::Type InFeatureLevel)
{
// this function will be called every time the feature level will be updated and some textures require a minimum feature level to exist
// the below declared variable (CurrentFeatureLevel) will guard against reinitialization of those textures already created in a previous call
// if FeatureLevelInitializedTo has its default value (ERHIFeatureLevel::Num) it means that setup was never performed and all textures are invalid
// thus CurrentFeatureLevel will be set to ERHIFeatureLevel::ES2_REMOVED to validate all 'is valid' branching conditions below
ERHIFeatureLevel::Type CurrentFeatureLevel = FeatureLevelInitializedTo == ERHIFeatureLevel::Num ? ERHIFeatureLevel::ES2_REMOVED : FeatureLevelInitializedTo;
// Create the SobolSampling texture
if (CurrentFeatureLevel < ERHIFeatureLevel::ES3_1 && InFeatureLevel >= ERHIFeatureLevel::ES3_1 && GPixelFormats[PF_R16_UINT].Supported)
{
FPooledRenderTargetDesc Desc(FPooledRenderTargetDesc::Create2DDesc(FIntPoint(32, 16), PF_R16_UINT, FClearValueBinding::None, TexCreate_HideInVisualizeTexture, TexCreate_NoFastClear | TexCreate_ShaderResource, false));
Desc.AutoWritable = false;
GRenderTargetPool.FindFreeElement(RHICmdList, Desc, SobolSampling, TEXT("SobolSampling"));
// Write the contents of the texture.
uint32 DestStride;
uint8* DestBuffer = (uint8*)RHICmdList.LockTexture2D((FTexture2DRHIRef&)SobolSampling->GetRenderTargetItem().ShaderResourceTexture, 0, RLM_WriteOnly, DestStride, false);
uint16 *Dest;
for (int y = 0; y < 16; ++y)
{
Dest = (uint16*)(DestBuffer + y * DestStride);
// 16x16 block starting at 0,0 = Sobol X,Y from bottom 4 bits of cell X,Y
for (int x = 0; x < 16; ++x, ++Dest)
{
*Dest = FSobol::ComputeGPUSpatialSeed(x, y, /* Index = */ 0);
}
// 16x16 block starting at 16,0 = Sobol X,Y from 2nd 4 bits of cell X,Y
for (int x = 0; x < 16; ++x, ++Dest)
{
*Dest = FSobol::ComputeGPUSpatialSeed(x, y, /* Index = */ 1);
}
}
RHICmdList.UnlockTexture2D((FTexture2DRHIRef&)SobolSampling->GetRenderTargetItem().ShaderResourceTexture, 0, false);
}
// Create a VolumetricBlackDummy texture
if (CurrentFeatureLevel < ERHIFeatureLevel::SM5 && InFeatureLevel >= ERHIFeatureLevel::SM5)
{
FPooledRenderTargetDesc Desc(FPooledRenderTargetDesc::CreateVolumeDesc(1, 1, 1, PF_B8G8R8A8, FClearValueBinding::Transparent, TexCreate_HideInVisualizeTexture, TexCreate_ShaderResource | TexCreate_RenderTargetable | TexCreate_NoFastClear, false));
Desc.AutoWritable = false;
GRenderTargetPool.FindFreeElement(RHICmdList, Desc, VolumetricBlackDummy, TEXT("VolumetricBlackDummy"), ERenderTargetTransience::NonTransient);
const uint8 BlackBytes[4] = { 0, 0, 0, 0 };
FUpdateTextureRegion3D Region(0, 0, 0, 0, 0, 0, Desc.Extent.X, Desc.Extent.Y, Desc.Depth);
RHICmdList.UpdateTexture3D(
(FTexture3DRHIRef&)VolumetricBlackDummy->GetRenderTargetItem().ShaderResourceTexture,
0,
Region,
Desc.Extent.X * sizeof(BlackBytes),
Desc.Extent.X * Desc.Extent.Y * sizeof(BlackBytes),
BlackBytes);
// UpdateTexture3D before and after state is currently undefined
RHICmdList.Transition(FRHITransitionInfo(VolumetricBlackDummy->GetTargetableRHI(), ERHIAccess::Unknown, ERHIAccess::SRVMask));
}
if (CurrentFeatureLevel < ERHIFeatureLevel::SM5 && InFeatureLevel >= ERHIFeatureLevel::SM5)
{
FPooledRenderTargetDesc Desc(FPooledRenderTargetDesc::CreateVolumeDesc(1, 1, 1, PF_B8G8R8A8, FClearValueBinding::Transparent, TexCreate_HideInVisualizeTexture, TexCreate_ShaderResource | TexCreate_RenderTargetable | TexCreate_NoFastClear, false));
Desc.AutoWritable = false;
GRenderTargetPool.FindFreeElement(RHICmdList, Desc, HairLUT0, TEXT("HairLUT0"), ERenderTargetTransience::NonTransient);
// Init with dummy textures. The texture will be initialize with real values if needed
const uint8 BlackBytes[4] = { 0, 0, 0, 0 };
FUpdateTextureRegion3D Region(0, 0, 0, 0, 0, 0, Desc.Extent.X, Desc.Extent.Y, Desc.Depth);
RHICmdList.UpdateTexture3D((FTexture3DRHIRef&)HairLUT0->GetRenderTargetItem().ShaderResourceTexture, 0, Region, Desc.Extent.X * sizeof(BlackBytes), Desc.Extent.X * Desc.Extent.Y * sizeof(BlackBytes), BlackBytes);
// UpdateTexture3D before and after state is currently undefined
RHICmdList.Transition(FRHITransitionInfo(HairLUT0->GetRenderTargetItem().ShaderResourceTexture, ERHIAccess::Unknown, ERHIAccess::SRVMask));
HairLUT1 = HairLUT0;
HairLUT2 = HairLUT0;
}
// The PreintegratedGF maybe used on forward shading inluding mobile platorm, intialize it anyway.
{
// for testing, with 128x128 R8G8 we are very close to the reference (if lower res is needed we might have to add an offset to counter the 0.5f texel shift)
const bool bReference = false;
EPixelFormat Format = PF_R8G8;
// for low roughness we would get banding with PF_R8G8 but for low spec it could be used, for now we don't do this optimization
if (GPixelFormats[PF_G16R16].Supported)
{
Format = PF_G16R16;
}
FPooledRenderTargetDesc Desc(FPooledRenderTargetDesc::Create2DDesc(FIntPoint(128, 32), Format, FClearValueBinding::None, TexCreate_None, TexCreate_ShaderResource, false));
Desc.AutoWritable = false;
if (bReference)
{
Desc.Extent.X = 128;
Desc.Extent.Y = 128;
}
GRenderTargetPool.FindFreeElement(RHICmdList, Desc, PreintegratedGF, TEXT("PreintegratedGF"), ERenderTargetTransience::NonTransient);
// Write the contents of the texture.
uint32 DestStride;
uint8* DestBuffer = (uint8*)RHICmdList.LockTexture2D((FTexture2DRHIRef&)PreintegratedGF->GetRenderTargetItem().ShaderResourceTexture, 0, RLM_WriteOnly, DestStride, false);
// x is NoV, y is roughness
for (int32 y = 0; y < Desc.Extent.Y; y++)
{
float Roughness = (float)(y + 0.5f) / Desc.Extent.Y;
float m = Roughness * Roughness;
float m2 = m * m;
for (int32 x = 0; x < Desc.Extent.X; x++)
{
float NoV = (float)(x + 0.5f) / Desc.Extent.X;
FVector V;
V.X = FMath::Sqrt(1.0f - NoV * NoV); // sin
V.Y = 0.0f;
V.Z = NoV; // cos
float A = 0.0f;
float B = 0.0f;
float C = 0.0f;
const uint32 NumSamples = 128;
for (uint32 i = 0; i < NumSamples; i++)
{
float E1 = (float)i / NumSamples;
float E2 = (double)ReverseBits(i) / (double)0x100000000LL;
{
float Phi = 2.0f * PI * E1;
float CosPhi = FMath::Cos(Phi);
float SinPhi = FMath::Sin(Phi);
float CosTheta = FMath::Sqrt((1.0f - E2) / (1.0f + (m2 - 1.0f) * E2));
float SinTheta = FMath::Sqrt(1.0f - CosTheta * CosTheta);
FVector H(SinTheta * FMath::Cos(Phi), SinTheta * FMath::Sin(Phi), CosTheta);
FVector L = 2.0f * (V | H) * H - V;
float NoL = FMath::Max(L.Z, 0.0f);
float NoH = FMath::Max(H.Z, 0.0f);
float VoH = FMath::Max(V | H, 0.0f);
if (NoL > 0.0f)
{
float Vis_SmithV = NoL * (NoV * (1 - m) + m);
float Vis_SmithL = NoV * (NoL * (1 - m) + m);
float Vis = 0.5f / (Vis_SmithV + Vis_SmithL);
float NoL_Vis_PDF = NoL * Vis * (4.0f * VoH / NoH);
float Fc = 1.0f - VoH;
Fc *= FMath::Square(Fc*Fc);
A += NoL_Vis_PDF * (1.0f - Fc);
B += NoL_Vis_PDF * Fc;
}
}
{
float Phi = 2.0f * PI * E1;
float CosPhi = FMath::Cos(Phi);
float SinPhi = FMath::Sin(Phi);
float CosTheta = FMath::Sqrt(E2);
float SinTheta = FMath::Sqrt(1.0f - CosTheta * CosTheta);
FVector L(SinTheta * FMath::Cos(Phi), SinTheta * FMath::Sin(Phi), CosTheta);
FVector H = (V + L).GetUnsafeNormal();
float NoL = FMath::Max(L.Z, 0.0f);
float NoH = FMath::Max(H.Z, 0.0f);
float VoH = FMath::Max(V | H, 0.0f);
float FD90 = 0.5f + 2.0f * VoH * VoH * Roughness;
float FdV = 1.0f + (FD90 - 1.0f) * pow(1.0f - NoV, 5);
float FdL = 1.0f + (FD90 - 1.0f) * pow(1.0f - NoL, 5);
C += FdV * FdL;// * ( 1.0f - 0.3333f * Roughness );
}
}
A /= NumSamples;
B /= NumSamples;
C /= NumSamples;
if (Desc.Format == PF_A16B16G16R16)
{
uint16* Dest = (uint16*)(DestBuffer + x * 8 + y * DestStride);
Dest[0] = (int32)(FMath::Clamp(A, 0.0f, 1.0f) * 65535.0f + 0.5f);
Dest[1] = (int32)(FMath::Clamp(B, 0.0f, 1.0f) * 65535.0f + 0.5f);
Dest[2] = (int32)(FMath::Clamp(C, 0.0f, 1.0f) * 65535.0f + 0.5f);
}
else if (Desc.Format == PF_G16R16)
{
uint16* Dest = (uint16*)(DestBuffer + x * 4 + y * DestStride);
Dest[0] = (int32)(FMath::Clamp(A, 0.0f, 1.0f) * 65535.0f + 0.5f);
Dest[1] = (int32)(FMath::Clamp(B, 0.0f, 1.0f) * 65535.0f + 0.5f);
}
else
{
check(Desc.Format == PF_R8G8);
uint8* Dest = (uint8*)(DestBuffer + x * 2 + y * DestStride);
Dest[0] = (int32)(FMath::Clamp(A, 0.0f, 1.0f) * 255.f + 0.5f);
Dest[1] = (int32)(FMath::Clamp(B, 0.0f, 1.0f) * 255.f + 0.5f);
}
}
}
RHICmdList.UnlockTexture2D((FTexture2DRHIRef&)PreintegratedGF->GetRenderTargetItem().ShaderResourceTexture, 0, false);
}
if (CurrentFeatureLevel < ERHIFeatureLevel::SM5 && InFeatureLevel >= ERHIFeatureLevel::SM5)
{
// Create the PerlinNoise3D texture (similar to http://prettyprocs.wordpress.com/2012/10/20/fast-perlin-noise/)
{
uint32 Extent = 16;
const uint32 Square = Extent * Extent;
FPooledRenderTargetDesc Desc(FPooledRenderTargetDesc::CreateVolumeDesc(Extent, Extent, Extent, PF_B8G8R8A8, FClearValueBinding::None, TexCreate_ShaderResource | TexCreate_HideInVisualizeTexture | TexCreate_NoTiling, TexCreate_ShaderResource, false));
Desc.AutoWritable = false;
GRenderTargetPool.FindFreeElement(RHICmdList, Desc, PerlinNoise3D, TEXT("PerlinNoise3D"), ERenderTargetTransience::NonTransient);
// Write the contents of the texture.
TArray<uint32> DestBuffer;
DestBuffer.AddZeroed(Extent * Extent * Extent);
// seed the pseudo random stream with a good value
FRandomStream RandomStream(0x1234);
// Values represent float3 values in the -1..1 range.
// The vectors are the edge mid point of a cube from -1 .. 1
// -1:0 0:7f 1:fe, can be reconstructed with * 512/254 - 1
// * 2 - 1 cannot be used because 0 would not be mapped
static uint32 gradtable[] =
{
0x7ffefe, 0xfe7ffe, 0xfefe7f,
0x7ffe00, 0xfe7f00, 0xfe007f,
0x7f00fe, 0x007ffe, 0x00fe7f,
0x7f0000, 0x007f00, 0x00007f,
};
// set random directions
{
for (uint32 z = 0; z < Extent - 1; ++z)
{
for (uint32 y = 0; y < Extent - 1; ++y)
{
for (uint32 x = 0; x < Extent - 1; ++x)
{
uint32& Value = DestBuffer[x + y * Extent + z * Square];
// pick a random direction (hacky way to overcome the quality issues FRandomStream has)
Value = gradtable[(uint32)(RandomStream.GetFraction() * 11.9999999f)];
}
}
}
}
// replicate a border for filtering
{
uint32 Last = Extent - 1;
for (uint32 z = 0; z < Extent; ++z)
{
for (uint32 y = 0; y < Extent; ++y)
{
DestBuffer[Last + y * Extent + z * Square] = DestBuffer[0 + y * Extent + z * Square];
}
}
for (uint32 z = 0; z < Extent; ++z)
{
for (uint32 x = 0; x < Extent; ++x)
{
DestBuffer[x + Last * Extent + z * Square] = DestBuffer[x + 0 * Extent + z * Square];
}
}
for (uint32 y = 0; y < Extent; ++y)
{
for (uint32 x = 0; x < Extent; ++x)
{
DestBuffer[x + y * Extent + Last * Square] = DestBuffer[x + y * Extent + 0 * Square];
}
}
}
// precompute gradients
{
uint32* Dest = DestBuffer.GetData();
for (uint32 z = 0; z < Desc.Depth; ++z)
{
for (uint32 y = 0; y < (uint32)Desc.Extent.Y; ++y)
{
for (uint32 x = 0; x < (uint32)Desc.Extent.X; ++x)
{
uint32 Value = *Dest;
// todo: check if rgb order is correct
int32 r = Value >> 16;
int32 g = (Value >> 8) & 0xff;
int32 b = Value & 0xff;
int nx = (r / 0x7f) - 1;
int ny = (g / 0x7f) - 1;
int nz = (b / 0x7f) - 1;
int32 d = nx * x + ny * y + nz * z;
// compress in 8bit
uint32 a = d + 127;
*Dest++ = Value | (a << 24);
}
}
}
}
FUpdateTextureRegion3D Region(0, 0, 0, 0, 0, 0, Desc.Extent.X, Desc.Extent.Y, Desc.Depth);
RHICmdList.UpdateTexture3D(
(FTexture3DRHIRef&)PerlinNoise3D->GetRenderTargetItem().ShaderResourceTexture,
0,
Region,
Desc.Extent.X * sizeof(uint32),
Desc.Extent.X * Desc.Extent.Y * sizeof(uint32),
(const uint8*)DestBuffer.GetData());
} // end Create the PerlinNoise3D texture
// GTAO Randomization texture
{
{
FPooledRenderTargetDesc Desc(FPooledRenderTargetDesc::Create2DDesc(FIntPoint(LTC_Size, LTC_Size), PF_FloatRGBA, FClearValueBinding::None, TexCreate_FastVRAM, TexCreate_ShaderResource, false));
Desc.AutoWritable = false;
GRenderTargetPool.FindFreeElement(RHICmdList, Desc, LTCMat, TEXT("LTCMat"));
// Write the contents of the texture.
uint32 DestStride;
uint8* DestBuffer = (uint8*)RHICmdList.LockTexture2D((FTexture2DRHIRef&)LTCMat->GetRenderTargetItem().ShaderResourceTexture, 0, RLM_WriteOnly, DestStride, false);
for (int32 y = 0; y < Desc.Extent.Y; ++y)
{
for (int32 x = 0; x < Desc.Extent.X; ++x)
{
uint16* Dest = (uint16*)(DestBuffer + x * 4 * sizeof(uint16) + y * DestStride);
for (int k = 0; k < 4; k++)
Dest[k] = FFloat16(LTC_Mat[4 * (x + y * LTC_Size) + k]).Encoded;
}
}
RHICmdList.UnlockTexture2D((FTexture2DRHIRef&)LTCMat->GetRenderTargetItem().ShaderResourceTexture, 0, false);
}
{
FPooledRenderTargetDesc Desc(FPooledRenderTargetDesc::Create2DDesc(FIntPoint(LTC_Size, LTC_Size), PF_G16R16F, FClearValueBinding::None, TexCreate_FastVRAM, TexCreate_ShaderResource, false));
Desc.AutoWritable = false;
GRenderTargetPool.FindFreeElement(RHICmdList, Desc, LTCAmp, TEXT("LTCAmp"));
// Write the contents of the texture.
uint32 DestStride;
uint8* DestBuffer = (uint8*)RHICmdList.LockTexture2D((FTexture2DRHIRef&)LTCAmp->GetRenderTargetItem().ShaderResourceTexture, 0, RLM_WriteOnly, DestStride, false);
for (int32 y = 0; y < Desc.Extent.Y; ++y)
{
for (int32 x = 0; x < Desc.Extent.X; ++x)
{
uint16* Dest = (uint16*)(DestBuffer + x * 2 * sizeof(uint16) + y * DestStride);
for (int k = 0; k < 2; k++)
Dest[k] = FFloat16(LTC_Amp[4 * (x + y * LTC_Size) + k]).Encoded;
}
}
RHICmdList.UnlockTexture2D((FTexture2DRHIRef&)LTCAmp->GetRenderTargetItem().ShaderResourceTexture, 0, false);
}
} // end Create the GTAO randomization texture
} // end if (FeatureLevelInitializedTo < ERHIFeatureLevel::SM5 && InFeatureLevel >= ERHIFeatureLevel::SM5)
// Create the SSAO randomization texture
static const auto MobileAmbientOcclusionCVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Mobile.AmbientOcclusion"));
if ((CurrentFeatureLevel < ERHIFeatureLevel::SM5 && InFeatureLevel >= ERHIFeatureLevel::SM5) ||
(CurrentFeatureLevel < ERHIFeatureLevel::ES3_1 && InFeatureLevel >= ERHIFeatureLevel::ES3_1 && MobileAmbientOcclusionCVar != nullptr && MobileAmbientOcclusionCVar->GetValueOnAnyThread()>0))
{
{
float g_AngleOff1 = 127;
float g_AngleOff2 = 198;
float g_AngleOff3 = 23;
FColor Bases[16];
for (int32 Pos = 0; Pos < 16; ++Pos)
{
// distribute rotations over 4x4 pattern
// int32 Reorder[16] = { 0, 8, 2, 10, 12, 6, 14, 4, 3, 11, 1, 9, 15, 5, 13, 7 };
int32 Reorder[16] = { 0, 11, 7, 3, 10, 4, 15, 12, 6, 8, 1, 14, 13, 2, 9, 5 };
int32 w = Reorder[Pos];
// ordered sampling of the rotation basis (*2 is missing as we use mirrored samples)
float ww = w / 16.0f * PI;
// randomize base scale
float lenm = 1.0f - (FMath::Sin(g_AngleOff2 * w * 0.01f) * 0.5f + 0.5f) * g_AngleOff3 * 0.01f;
float s = FMath::Sin(ww) * lenm;
float c = FMath::Cos(ww) * lenm;
Bases[Pos] = FColor(FMath::Quantize8SignedByte(c), FMath::Quantize8SignedByte(s), 0, 0);
}
{
// could be PF_V8U8 to save shader instructions but that doesn't work on all hardware
FPooledRenderTargetDesc Desc(FPooledRenderTargetDesc::Create2DDesc(FIntPoint(64, 64), PF_R8G8, FClearValueBinding::None, TexCreate_HideInVisualizeTexture, TexCreate_NoFastClear | TexCreate_ShaderResource, false));
Desc.AutoWritable = false;
GRenderTargetPool.FindFreeElement(RHICmdList, Desc, SSAORandomization, TEXT("SSAORandomization"), ERenderTargetTransience::NonTransient);
// Write the contents of the texture.
uint32 DestStride;
uint8* DestBuffer = (uint8*)RHICmdList.LockTexture2D((FTexture2DRHIRef&)SSAORandomization->GetRenderTargetItem().ShaderResourceTexture, 0, RLM_WriteOnly, DestStride, false);
for (int32 y = 0; y < Desc.Extent.Y; ++y)
{
for (int32 x = 0; x < Desc.Extent.X; ++x)
{
uint8* Dest = (uint8*)(DestBuffer + x * sizeof(uint16) + y * DestStride);
uint32 Index = (x % 4) + (y % 4) * 4;
Dest[0] = Bases[Index].R;
Dest[1] = Bases[Index].G;
}
}
}
RHICmdList.UnlockTexture2D((FTexture2DRHIRef&)SSAORandomization->GetRenderTargetItem().ShaderResourceTexture, 0, false);
}
}
static const auto MobileGTAOPreIntegratedTextureTypeCVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Mobile.GTAOPreIntegratedTextureType"));
if (CurrentFeatureLevel < ERHIFeatureLevel::ES3_1 && InFeatureLevel >= ERHIFeatureLevel::ES3_1 && MobileGTAOPreIntegratedTextureTypeCVar && MobileGTAOPreIntegratedTextureTypeCVar->GetValueOnAnyThread() > 0)
{
uint32 Extent = 16; // should be consistent with LUTSize in PostprocessMobile.usf
const uint32 Square = Extent * Extent;
bool bGTAOPreIngegratedUsingVolumeLUT = MobileGTAOPreIntegratedTextureTypeCVar->GetValueOnAnyThread() == 2;
FPooledRenderTargetDesc Desc;
if (bGTAOPreIngegratedUsingVolumeLUT)
{
Desc = FPooledRenderTargetDesc(FPooledRenderTargetDesc::CreateVolumeDesc(Extent, Extent, Extent, PF_R16F, FClearValueBinding::None, TexCreate_HideInVisualizeTexture | TexCreate_NoTiling | TexCreate_ShaderResource, TexCreate_ShaderResource, false));
}
else
{
Desc = FPooledRenderTargetDesc(FPooledRenderTargetDesc::Create2DDesc(FIntPoint(Square, Extent), PF_R16F, FClearValueBinding::None, TexCreate_HideInVisualizeTexture | TexCreate_NoTiling | TexCreate_ShaderResource, TexCreate_ShaderResource, false));
}
Desc.AutoWritable = false;
GRenderTargetPool.FindFreeElement(RHICmdList, Desc, GTAOPreIntegrated, TEXT("GTAOPreIntegrated"), ERenderTargetTransience::NonTransient);
// Write the contents of the texture.
TArray<FFloat16> TempBuffer;
TempBuffer.AddZeroed(Extent * Extent * Extent);
FFloat16* DestBuffer = nullptr;
if (bGTAOPreIngegratedUsingVolumeLUT)
{
DestBuffer = TempBuffer.GetData();
}
else
{
uint32 DestStride;
DestBuffer = (FFloat16*)RHICmdList.LockTexture2D((FTexture2DRHIRef&)GTAOPreIntegrated->GetRenderTargetItem().ShaderResourceTexture, 0, RLM_WriteOnly, DestStride, false);
}
for (uint32 z = 0; z < Extent; ++z)
{
for (uint32 y = 0; y < Extent; ++y)
{
for (uint32 x = 0; x < Extent; ++x)
{
uint32 DestBufferIndex = 0;
if (bGTAOPreIngegratedUsingVolumeLUT)
{
DestBufferIndex = x + y * Extent + z * Square;
}
else
{
DestBufferIndex = (x + z * Extent) + y * Square;
}
FFloat16& Value = DestBuffer[DestBufferIndex];
float cosAngle1 = ((x + 0.5f) / (Extent) - 0.5f) * 2;
float cosAngle2 = ((y + 0.5f) / (Extent) - 0.5f) * 2;
float cosAng = ((z + 0.5f) / (Extent) - 0.5f) * 2;
float Gamma = FMath::Acos(cosAng) - HALF_PI;
float CosGamma = FMath::Cos(Gamma);
float SinGamma = cosAng * -2.0f;
float Angle1 = FMath::Acos(cosAngle1);
float Angle2 = FMath::Acos(cosAngle2);
// clamp to normal hemisphere
Angle1 = Gamma + FMath::Max(-Angle1 - Gamma, -(HALF_PI));
Angle2 = Gamma + FMath::Min(Angle2 - Gamma, (HALF_PI));
float AO = (0.25f *
((Angle1 * SinGamma + CosGamma - cos((2.0 * Angle1) - Gamma)) +
(Angle2 * SinGamma + CosGamma - cos((2.0 * Angle2) - Gamma))));
Value = AO;
}
}
}
if (bGTAOPreIngegratedUsingVolumeLUT)
{
FUpdateTextureRegion3D Region(0, 0, 0, 0, 0, 0, Desc.Extent.X, Desc.Extent.Y, Desc.Depth);
RHICmdList.UpdateTexture3D(
(FTexture3DRHIRef&)GTAOPreIntegrated->GetRenderTargetItem().ShaderResourceTexture,
0,
Region,
Desc.Extent.X * sizeof(FFloat16),
Desc.Extent.X * Desc.Extent.Y * sizeof(FFloat16),
(const uint8*)DestBuffer);
}
else
{
RHICmdList.UnlockTexture2D((FTexture2DRHIRef&)GTAOPreIntegrated->GetRenderTargetItem().ShaderResourceTexture, 0, false);
}
}
// Initialize textures only once.
FeatureLevelInitializedTo = InFeatureLevel;
}
void FSystemTextures::ReleaseDynamicRHI()
{
WhiteDummySRV.SafeRelease();
WhiteDummy.SafeRelease();
BlackDummy.SafeRelease();
BlackAlphaOneDummy.SafeRelease();
PerlinNoiseGradient.SafeRelease();
PerlinNoise3D.SafeRelease();
SobolSampling.SafeRelease();
SSAORandomization.SafeRelease();
GTAOPreIntegrated.SafeRelease();
PreintegratedGF.SafeRelease();
HairLUT0.SafeRelease();
HairLUT1.SafeRelease();
HairLUT2.SafeRelease();
LTCMat.SafeRelease();
LTCAmp.SafeRelease();
MaxFP16Depth.SafeRelease();
DepthDummy.SafeRelease();
GreenDummy.SafeRelease();
DefaultNormal8Bit.SafeRelease();
VolumetricBlackDummy.SafeRelease();
ZeroUIntDummy.SafeRelease();
ZeroUShort4Dummy.SafeRelease();
MidGreyDummy.SafeRelease();
StencilDummy.SafeRelease();
StencilDummySRV.SafeRelease();
GTAOPreIntegrated.SafeRelease();
GRenderTargetPool.FreeUnusedResources();
// Indicate that textures will need to be reinitialized.
FeatureLevelInitializedTo = ERHIFeatureLevel::Num;
}
FRDGTextureRef FSystemTextures::GetBlackDummy(FRDGBuilder& GraphBuilder) const
{
return GraphBuilder.RegisterExternalTexture(BlackDummy, TEXT("BlackDummy"));
}
FRDGTextureRef FSystemTextures::GetBlackAlphaOneDummy(FRDGBuilder& GraphBuilder) const
{
return GraphBuilder.RegisterExternalTexture(BlackAlphaOneDummy, TEXT("BlackAlphaOneDummy"));
}
FRDGTextureRef FSystemTextures::GetWhiteDummy(FRDGBuilder& GraphBuilder) const
{
return GraphBuilder.RegisterExternalTexture(WhiteDummy, TEXT("WhiteDummy"));
}
FRDGTextureRef FSystemTextures::GetMaxFP16Depth(FRDGBuilder& GraphBuilder) const
{
return GraphBuilder.RegisterExternalTexture(MaxFP16Depth, TEXT("MaxFP16Depth"));
}
FRDGTextureRef FSystemTextures::GetDepthDummy(FRDGBuilder& GraphBuilder) const
{
return GraphBuilder.RegisterExternalTexture(DepthDummy, TEXT("DepthDummy"));
}
FRDGTextureRef FSystemTextures::GetStencilDummy(FRDGBuilder& GraphBuilder) const
{
return GraphBuilder.RegisterExternalTexture(StencilDummy, TEXT("StencilDummy"));
}
FRDGTextureRef FSystemTextures::GetGreenDummy(FRDGBuilder& GraphBuilder) const
{
return GraphBuilder.RegisterExternalTexture(GreenDummy, TEXT("GreenDummy"));
}
FRDGTextureRef FSystemTextures::GetDefaultNormal8Bit(FRDGBuilder& GraphBuilder) const
{
return GraphBuilder.RegisterExternalTexture(DefaultNormal8Bit, TEXT("DefaultNormal8Bit"));
}
FRDGTextureRef FSystemTextures::GetMidGreyDummy(FRDGBuilder& GraphBuilder) const
{
return GraphBuilder.RegisterExternalTexture(MidGreyDummy, TEXT("MidGreyDummy"));
}
FRDGTextureRef FSystemTextures::GetVolumetricBlackDummy(FRDGBuilder& GraphBuilder) const
{
return GraphBuilder.RegisterExternalTexture(VolumetricBlackDummy, TEXT("VolumetricBlackDummy"));
}
FRDGTextureRef FSystemTextures::GetZeroUIntDummy(FRDGBuilder& GraphBuilder) const
{
return GraphBuilder.RegisterExternalTexture(ZeroUIntDummy, TEXT("ZeroUIntDummy"));
}
FRDGTextureRef FSystemTextures::GetZeroUShort4Dummy(FRDGBuilder& GraphBuilder) const
{
return GraphBuilder.RegisterExternalTexture(ZeroUShort4Dummy, TEXT("ZeroUShort4Dummy"));
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// Default textures
template<typename TClearType>
FClearValueBinding GetClearBinding(TClearType Value)
{
return FClearValueBinding::None;
}
template<>
FClearValueBinding GetClearBinding(FClearValueBinding Value)
{
return Value;
}
bool operator !=(const FDefaultTextureKey& A, const FDefaultTextureKey& B)
{
return A.Format != B.Format ||
A.Dimension != B.Dimension ||
A.ValueAsUInt[0] != B.ValueAsUInt[0] ||
A.ValueAsUInt[1] != B.ValueAsUInt[1] ||
A.ValueAsUInt[2] != B.ValueAsUInt[2] ||
A.ValueAsUInt[3] != B.ValueAsUInt[3];
}
template<typename T>
static FDefaultTextureKey GetDefaultTextureKey(EPixelFormat Format, const T& In)
{
FDefaultTextureKey Out;
const uint32 Size = sizeof(T);
const uint32* InAsUInt = (const uint32*)&In;
Out.ValueAsUInt[0] = InAsUInt[0];
Out.ValueAsUInt[1] = Size > 4 ? InAsUInt[1] : 0u;
Out.ValueAsUInt[2] = Size > 8 ? InAsUInt[2] : 0u;
Out.ValueAsUInt[3] = Size > 12 ? InAsUInt[3] : 0u;
Out.Format = Format;
return Out;
}
static void AddClearUAVPass(FRDGBuilder& GraphBuilder, FRDGTextureUAVRef TextureUAV, const FClearValueBinding& Value)
{
// Nothing. This is just for compiling template function
}
template<typename TClearValue>
FRDGTextureRef GetInternalDefaultTexture(
FRDGBuilder& GraphBuilder,
TArray<FDefaultTexture>& DefaultTextures,
FHashTable& HashDefaultTextures,
ETextureDimension Dimension,
EPixelFormat Format,
TClearValue Value)
{
// Check this is a valid format
check(Format != PF_Unknown && Format != PF_MAX && GPixelFormats[Format].BlockSizeX == 1 && GPixelFormats[Format].BlockSizeY == 1 && GPixelFormats[Format].BlockSizeZ == 1);
const FDefaultTextureKey Key = GetDefaultTextureKey(Format, Value);
const uint32 Hash = Murmur32({uint32(Key.Dimension), uint32(Key.Format), Key.ValueAsUInt[0], Key.ValueAsUInt[1], Key.ValueAsUInt[2], Key.ValueAsUInt[3]});
uint32 Index = HashDefaultTextures.First(Hash);
while (HashDefaultTextures.IsValid(Index) && DefaultTextures[Index].Key != Key)
{
Index = HashDefaultTextures.Next(Index);
check(DefaultTextures[Index].Hash == Hash); //Sanitycheck
}
if (HashDefaultTextures.IsValid(Index) && DefaultTextures[Index].Texture != nullptr)
{
return GraphBuilder.RegisterExternalTexture(DefaultTextures[Index].Texture);
}
bool bSupportUAVWrite = true;
bool bSupportGraphicsWrite = true;
FClearValueBinding ClearValue = GetClearBinding(Value);
if (IsDepthOrStencilFormat(Format))
{
check(
ClearValue == FClearValueBinding::DepthZero ||
ClearValue == FClearValueBinding::DepthOne ||
ClearValue == FClearValueBinding::DepthFar ||
ClearValue == FClearValueBinding::DepthNear);
bSupportUAVWrite = false;
}
ETextureCreateFlags Flags =
TexCreate_ShaderResource |
(bSupportUAVWrite ? TexCreate_UAV : TexCreate_None) |
(bSupportGraphicsWrite ? TexCreate_RenderTargetable : TexCreate_None);
FRDGTextureRef Texture = nullptr;
if (Dimension == ETextureDimension::Texture2D)
{
Texture = GraphBuilder.CreateTexture(FRDGTextureDesc::Create2D(FIntPoint(1, 1), Format, ClearValue, TexCreate_ShaderResource | Flags), TEXT("DefaultTexture"), ERDGTextureFlags::MultiFrame); // TODO Create better name e.g., DefaultTexture(2D,PF_R32INT,Zero)
}
else if (Dimension == ETextureDimension::Texture2DArray)
{
Texture = GraphBuilder.CreateTexture(FRDGTextureDesc::Create2DArray(FIntPoint(1, 1), Format, ClearValue, TexCreate_ShaderResource | Flags, 1), TEXT("DefaultTexture"), ERDGTextureFlags::MultiFrame);
}
else if (Dimension == ETextureDimension::Texture3D)
{
Texture = GraphBuilder.CreateTexture(FRDGTextureDesc::Create3D(FIntVector(1, 1, 1), Format, ClearValue, TexCreate_ShaderResource | Flags), TEXT("DefaultTexture"), ERDGTextureFlags::MultiFrame);
}
else if (Dimension == ETextureDimension::TextureCube)
{
Texture = GraphBuilder.CreateTexture(FRDGTextureDesc::CreateCube(1, Format, ClearValue, TexCreate_ShaderResource | Flags), TEXT("DefaultTexture"), ERDGTextureFlags::MultiFrame);
}
else if (Dimension == ETextureDimension::TextureCubeArray)
{
Texture = GraphBuilder.CreateTexture(FRDGTextureDesc::CreateCubeArray(1, Format, ClearValue, TexCreate_ShaderResource | Flags, 1), TEXT("DefaultTexture"), ERDGTextureFlags::MultiFrame);
}
else
{
return nullptr;
}
if (ClearValue == FClearValueBinding::None)
{
check(bSupportUAVWrite);
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(Texture), Value);
}
FDefaultTexture Entry;
Entry.Key = Key;
Entry.Hash = Hash;
Entry.Texture = GraphBuilder.ConvertToExternalTexture(Texture);
Index = DefaultTextures.Add(Entry);
HashDefaultTextures.Add(Hash, Index);
return Texture;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// Default Buffers
template<typename T>
static FDefaultBufferKey GetDefaultBufferKey(uint32 NumBytePerElement, bool bIsStructuredBuffer, const T* In)
{
FDefaultBufferKey Out;
if (In)
{
const uint32* InAsUInt = (const uint32*)In;
Out.ValueAsUInt[0] = InAsUInt[0];
Out.ValueAsUInt[1] = NumBytePerElement > 4 ? InAsUInt[1] : 0u;
Out.ValueAsUInt[2] = NumBytePerElement > 8 ? InAsUInt[2] : 0u;
Out.ValueAsUInt[3] = NumBytePerElement > 12 ? InAsUInt[3] : 0u;
}
Out.NumBytePerElement = NumBytePerElement;
Out.bIsStructuredBuffer = bIsStructuredBuffer;
return Out;
}
bool operator !=(const FDefaultBufferKey& A, const FDefaultBufferKey& B)
{
return A.NumBytePerElement != B.NumBytePerElement ||
A.bIsStructuredBuffer != B.bIsStructuredBuffer ||
A.ValueAsUInt[0] != B.ValueAsUInt[0] ||
A.ValueAsUInt[1] != B.ValueAsUInt[1] ||
A.ValueAsUInt[2] != B.ValueAsUInt[2] ||
A.ValueAsUInt[3] != B.ValueAsUInt[3];
}
template<typename TClearValue>
FRDGBufferRef GetInternalDefaultBuffer(
FRDGBuilder& GraphBuilder,
TArray<FDefaultBuffer>& DefaultBuffers,
FHashTable& HashDefaultBuffers,
uint32 NumBytePerElement,
bool bIsStructuredBuffer,
const TClearValue* Value)
{
// Buffer key
const uint32 NumElements = 1;
const FDefaultBufferKey Key = GetDefaultBufferKey(NumBytePerElement, bIsStructuredBuffer, Value);
const uint32 Hash = Murmur32({uint32(Key.bIsStructuredBuffer ? 0x20000000u : 0x10000000u) | Key.NumBytePerElement, Key.ValueAsUInt[0], Key.ValueAsUInt[1], Key.ValueAsUInt[2], Key.ValueAsUInt[3] });
// Find exsting buffer ("fast" path)
uint32 Index = HashDefaultBuffers.First(Hash);
while (HashDefaultBuffers.IsValid(Index) && DefaultBuffers[Index].Key != Key)
{
Index = HashDefaultBuffers.Next(Index);
check(DefaultBuffers[Index].Hash == Hash); //Sanitycheck
}
if (HashDefaultBuffers.IsValid(Index) && DefaultBuffers[Index].Buffer != nullptr)
{
return GraphBuilder.RegisterExternalBuffer(DefaultBuffers[Index].Buffer);
}
// Adding new buffer if there is no fit (slow path)
FRDGBufferRef Buffer = nullptr;
if (bIsStructuredBuffer)
{
Buffer = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(NumBytePerElement, NumElements), TEXT("DefaultBuffer"), ERDGBufferFlags::MultiFrame);
}
else
{
Buffer = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateBufferDesc(NumBytePerElement, NumElements), TEXT("DefaultStructuredBuffer"), ERDGBufferFlags::MultiFrame);
}
// Initialize the entire buffer with the provided data
if (Value)
{
AddBufferUploadPass(GraphBuilder, Buffer, Value, NumElements * NumBytePerElement, ERDGInitialDataFlags::None);
}
// Initialize buffer to 0
else
{
TArray<uint8> DefaultValue;
DefaultValue.Init(0u, NumElements * NumBytePerElement);
AddBufferUploadPass(GraphBuilder, Buffer, DefaultValue.GetData(), DefaultValue.Num(), ERDGInitialDataFlags::None);
}
FDefaultBuffer Entry;
Entry.Key = Key;
Entry.Hash = Hash;
Entry.Buffer = GraphBuilder.ConvertToExternalBuffer(Buffer);
Index = DefaultBuffers.Add(Entry);
HashDefaultBuffers.Add(Hash, Index);
return Buffer;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Textures
FRDGTextureRef FSystemTextures::GetDefaultTexture2D(FRDGBuilder& GraphBuilder, EPixelFormat Format, float Value) { return GetInternalDefaultTexture(GraphBuilder, DefaultTextures, HashDefaultTextures, ETextureDimension::Texture2D, Format, Value); }
FRDGTextureRef FSystemTextures::GetDefaultTexture2D(FRDGBuilder& GraphBuilder, EPixelFormat Format, uint32 Value) { return GetInternalDefaultTexture(GraphBuilder, DefaultTextures, HashDefaultTextures, ETextureDimension::Texture2D, Format, Value); }
FRDGTextureRef FSystemTextures::GetDefaultTexture2D(FRDGBuilder& GraphBuilder, EPixelFormat Format, const FVector& Value) { return GetInternalDefaultTexture(GraphBuilder, DefaultTextures, HashDefaultTextures, ETextureDimension::Texture2D, Format, Value); }
FRDGTextureRef FSystemTextures::GetDefaultTexture2D(FRDGBuilder& GraphBuilder, EPixelFormat Format, const FVector4& Value) { return GetInternalDefaultTexture(GraphBuilder, DefaultTextures, HashDefaultTextures, ETextureDimension::Texture2D, Format, Value); }
FRDGTextureRef FSystemTextures::GetDefaultTexture2D(FRDGBuilder& GraphBuilder, EPixelFormat Format, const FUintVector4& Value) { return GetInternalDefaultTexture(GraphBuilder, DefaultTextures, HashDefaultTextures, ETextureDimension::Texture2D, Format, Value); }
FRDGTextureRef FSystemTextures::GetDefaultTexture2D(FRDGBuilder& GraphBuilder, EPixelFormat Format, const FClearValueBinding& Value) { return GetInternalDefaultTexture(GraphBuilder, DefaultTextures, HashDefaultTextures, ETextureDimension::Texture2D, Format, Value); }
FRDGTextureRef FSystemTextures::GetDefaultTexture(FRDGBuilder& GraphBuilder, ETextureDimension Dimension, EPixelFormat Format, float Value) { return GetInternalDefaultTexture(GraphBuilder, DefaultTextures, HashDefaultTextures, Dimension, Format, Value); }
FRDGTextureRef FSystemTextures::GetDefaultTexture(FRDGBuilder& GraphBuilder, ETextureDimension Dimension, EPixelFormat Format, uint32 Value) { return GetInternalDefaultTexture(GraphBuilder, DefaultTextures, HashDefaultTextures, Dimension, Format, Value); }
FRDGTextureRef FSystemTextures::GetDefaultTexture(FRDGBuilder& GraphBuilder, ETextureDimension Dimension, EPixelFormat Format, const FVector2D& Value) { return GetInternalDefaultTexture(GraphBuilder, DefaultTextures, HashDefaultTextures, Dimension, Format, Value); }
FRDGTextureRef FSystemTextures::GetDefaultTexture(FRDGBuilder& GraphBuilder, ETextureDimension Dimension, EPixelFormat Format, const FIntPoint& Value) { return GetInternalDefaultTexture(GraphBuilder, DefaultTextures, HashDefaultTextures, Dimension, Format, Value); }
FRDGTextureRef FSystemTextures::GetDefaultTexture(FRDGBuilder& GraphBuilder, ETextureDimension Dimension, EPixelFormat Format, const FVector& Value) { return GetInternalDefaultTexture(GraphBuilder, DefaultTextures, HashDefaultTextures, Dimension, Format, Value); }
FRDGTextureRef FSystemTextures::GetDefaultTexture(FRDGBuilder& GraphBuilder, ETextureDimension Dimension, EPixelFormat Format, const FVector4& Value) { return GetInternalDefaultTexture(GraphBuilder, DefaultTextures, HashDefaultTextures, Dimension, Format, Value); }
FRDGTextureRef FSystemTextures::GetDefaultTexture(FRDGBuilder& GraphBuilder, ETextureDimension Dimension, EPixelFormat Format, const FUintVector4& Value) { return GetInternalDefaultTexture(GraphBuilder, DefaultTextures, HashDefaultTextures, Dimension, Format, Value); }
FRDGTextureRef FSystemTextures::GetDefaultTexture(FRDGBuilder& GraphBuilder, ETextureDimension Dimension, EPixelFormat Format, const FClearValueBinding& Value) { return GetInternalDefaultTexture(GraphBuilder, DefaultTextures, HashDefaultTextures, Dimension, Format, Value); }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Buffers
// Default init to 0
FRDGBufferRef FSystemTextures::GetDefaultBuffer(FRDGBuilder& GraphBuilder, uint32 NumBytePerElement) { return GetInternalDefaultBuffer(GraphBuilder, DefaultBuffers, HashDefaultBuffers, NumBytePerElement, false, (uint32*)nullptr); }
FRDGBufferRef FSystemTextures::GetDefaultStructuredBuffer(FRDGBuilder& GraphBuilder, uint32 NumBytePerElement) { return GetInternalDefaultBuffer(GraphBuilder, DefaultBuffers, HashDefaultBuffers, NumBytePerElement, true, (uint32*)nullptr); }
// Default value of an element
FRDGBufferRef FSystemTextures::GetDefaultBuffer(FRDGBuilder& GraphBuilder, uint32 NumBytePerElement, float Value) { return GetInternalDefaultBuffer(GraphBuilder, DefaultBuffers, HashDefaultBuffers, NumBytePerElement, false/* Vertex */, &Value); }
FRDGBufferRef FSystemTextures::GetDefaultBuffer(FRDGBuilder& GraphBuilder, uint32 NumBytePerElement, uint32 Value) { return GetInternalDefaultBuffer(GraphBuilder, DefaultBuffers, HashDefaultBuffers, NumBytePerElement, false/* Vertex */, &Value); }
FRDGBufferRef FSystemTextures::GetDefaultBuffer(FRDGBuilder& GraphBuilder, uint32 NumBytePerElement, const FVector& Value) { return GetInternalDefaultBuffer(GraphBuilder, DefaultBuffers, HashDefaultBuffers, NumBytePerElement, false/* Vertex */, &Value); }
FRDGBufferRef FSystemTextures::GetDefaultBuffer(FRDGBuilder& GraphBuilder, uint32 NumBytePerElement, const FVector4& Value) { return GetInternalDefaultBuffer(GraphBuilder, DefaultBuffers, HashDefaultBuffers, NumBytePerElement, false/* Vertex */, &Value); }
FRDGBufferRef FSystemTextures::GetDefaultBuffer(FRDGBuilder& GraphBuilder, uint32 NumBytePerElement, const FUintVector4& Value) { return GetInternalDefaultBuffer(GraphBuilder, DefaultBuffers, HashDefaultBuffers, NumBytePerElement, false/* Vertex */, &Value); }
FRDGBufferRef FSystemTextures::GetDefaultStructuredBuffer(FRDGBuilder& GraphBuilder, uint32 NumBytePerElement, float Value) { return GetInternalDefaultBuffer(GraphBuilder, DefaultBuffers, HashDefaultBuffers, NumBytePerElement, true /* Structured */, &Value); }
FRDGBufferRef FSystemTextures::GetDefaultStructuredBuffer(FRDGBuilder& GraphBuilder, uint32 NumBytePerElement, uint32 Value) { return GetInternalDefaultBuffer(GraphBuilder, DefaultBuffers, HashDefaultBuffers, NumBytePerElement, true /* Structured */, &Value); }
FRDGBufferRef FSystemTextures::GetDefaultStructuredBuffer(FRDGBuilder& GraphBuilder, uint32 NumBytePerElement, const FVector& Value) { return GetInternalDefaultBuffer(GraphBuilder, DefaultBuffers, HashDefaultBuffers, NumBytePerElement, true /* Structured */, &Value); }
FRDGBufferRef FSystemTextures::GetDefaultStructuredBuffer(FRDGBuilder& GraphBuilder, uint32 NumBytePerElement, const FVector4& Value) { return GetInternalDefaultBuffer(GraphBuilder, DefaultBuffers, HashDefaultBuffers, NumBytePerElement, true /* Structured */, &Value); }
FRDGBufferRef FSystemTextures::GetDefaultStructuredBuffer(FRDGBuilder& GraphBuilder, uint32 NumBytePerElement, const FUintVector4& Value) { return GetInternalDefaultBuffer(GraphBuilder, DefaultBuffers, HashDefaultBuffers, NumBytePerElement, true /* Structured */, &Value); }