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

1788 lines
78 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 "Math/Float16.h"
#include "RenderTargetPool.h"
#include "ClearQuad.h"
#include "Math/PackedVector.h"
#include "GlobalRenderResources.h"
#include "Engine/Texture2D.h"
#include "Engine/Engine.h"
#include "Containers/ResourceArray.h"
/*-----------------------------------------------------------------------------
Export System textures (LTC / EnergyConservation / Hair LUT / ...)
-----------------------------------------------------------------------------*/
#define EXPORT_SYSTEM_TEXTURES 0
#if EXPORT_SYSTEM_TEXTURES
#include "LTC.h"
#include "ImageUtils.h"
static void SaveEXR(const FString& Filename, const FIntPoint& DataSize, const TArray<FFloat16Color>& Data)
{
const FString RelativeFilepath = FPaths::EngineSavedDir() + Filename + TEXT(".exr");
const FString AbsoluteFilepath = FPaths::ConvertRelativePathToFull(RelativeFilepath);
FImageView ImageView(Data.GetData(), DataSize.X, DataSize.Y);
const bool bSucceed = FImageUtils::SaveImageByExtension(*AbsoluteFilepath, ImageView);
check(bSucceed)
}
static void ExportSystemTextures()
{
// LTC Textures (used by Rect Lights and/or BSDF evaluation)
// GGX - LTC matrix coefficients (4-coefficients)
{
const FIntPoint DataSize(GGX_LTC_Size, GGX_LTC_Size);
TArray<FFloat16Color> RawPixels;
RawPixels.SetNum(DataSize.X * DataSize.Y);
for (int32 y = 0; y < GGX_LTC_Size; ++y)
{
for (int32 x = 0; x < GGX_LTC_Size; ++x)
{
FFloat16Color& Dest = RawPixels[x + y * DataSize.X];
Dest.R = FFloat16(GGX_LTC_Mat[4 * (x + y * GGX_LTC_Size) + 0]);
Dest.G = FFloat16(GGX_LTC_Mat[4 * (x + y * GGX_LTC_Size) + 1]);
Dest.B = FFloat16(GGX_LTC_Mat[4 * (x + y * GGX_LTC_Size) + 2]);
Dest.A = FFloat16(GGX_LTC_Mat[4 * (x + y * GGX_LTC_Size) + 3]);
}
}
SaveEXR(TEXT("GGX_LTCMat"), DataSize, RawPixels);
}
// GGX - Split-Sum Amplitude coefficients (2-components)
{
const FIntPoint DataSize(GGX_LTC_Size, GGX_LTC_Size);
TArray<FFloat16Color> RawPixels;
RawPixels.SetNum(DataSize.X * DataSize.Y);
for (int32 y = 0; y < GGX_LTC_Size; ++y)
{
for (int32 x = 0; x < GGX_LTC_Size; ++x)
{
FFloat16Color& Dest = RawPixels[x + y * DataSize.X];
Dest.R = FFloat16(GGX_LTC_Amp[4 * (x + y * GGX_LTC_Size) + 0]);
Dest.G = FFloat16(GGX_LTC_Amp[4 * (x + y * GGX_LTC_Size) + 1]);
Dest.B = 0;
Dest.A = 0;
}
}
SaveEXR(TEXT("GGX_LTCAmp"), DataSize, RawPixels);
}
// Sheen - Matrix & directional albedo (3-components)
{
const FIntPoint DataSize(Sheen_LTC_Size, Sheen_LTC_Size);
TArray<FFloat16Color> RawPixels;
RawPixels.SetNum(DataSize.X * DataSize.Y);
for (int32 y = 0; y < DataSize.Y; ++y)
{
for (int32 x = 0; x < DataSize.X; ++x)
{
FFloat16Color& Dest = RawPixels[x + y * DataSize.X];
Dest.R = FFloat16(Sheen_LTC_Volume[x][y][0]);
Dest.G = FFloat16(Sheen_LTC_Volume[x][y][1]);
Dest.B = FFloat16(Sheen_LTC_Volume[x][y][2]);
Dest.A = FFloat16(0);
}
}
SaveEXR(TEXT("Sheen_LTC"), DataSize, RawPixels);
}
}
#endif // EXPORT_SYSTEM_TEXTURES
/*-----------------------------------------------------------------------------
SystemTextures
-----------------------------------------------------------------------------*/
RDG_REGISTER_BLACKBOARD_STRUCT(FRDGSystemTextures);
const FRDGSystemTextures& FRDGSystemTextures::Create(FRDGBuilder& GraphBuilder)
{
const auto Register = [&](const TRefCountPtr<IPooledRenderTarget>& RenderTarget)
{
return TryRegisterExternalTexture(GraphBuilder, RenderTarget, ERDGTextureFlags::SkipTracking);
};
auto& SystemTextures = GraphBuilder.Blackboard.Create<FRDGSystemTextures>();
SystemTextures.White = Register(GSystemTextures.WhiteDummy);
SystemTextures.Black = Register(GSystemTextures.BlackDummy);
SystemTextures.BlackAlphaOne = Register(GSystemTextures.BlackAlphaOneDummy);
SystemTextures.BlackArray = Register(GSystemTextures.BlackArrayDummy);
SystemTextures.MaxFP16Depth = Register(GSystemTextures.MaxFP16Depth);
SystemTextures.DepthDummy = Register(GSystemTextures.DepthDummy);
SystemTextures.BlackDepthCube = Register(GSystemTextures.BlackDepthCube);
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.VolumetricBlackAlphaOne = Register(GSystemTextures.VolumetricBlackAlphaOneDummy);
SystemTextures.VolumetricBlackUint = Register(GSystemTextures.VolumetricBlackUintDummy);
SystemTextures.CubeBlack = Register(GSystemTextures.CubeBlackDummy);
SystemTextures.CubeArrayBlack = Register(GSystemTextures.CubeArrayBlackDummy);
SystemTextures.StencilDummySRV = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::Create(SystemTextures.StencilDummy));
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)
{
TRACE_CPUPROFILER_EVENT_SCOPE(FSystemTextures::InitializeTextures);
// 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
// Initialized resources depending on GEngine data
if (!bEngineDependentTexturesInitialized && GEngine)
{
InitializeEngineDependentTextures(RHICmdList);
}
}
const static FLazyName SystemTexturesName(TEXT("FSystemTextures"));
template <typename DataType>
static FTextureRHIRef CreateDummyTexture(FRHICommandListImmediate& RHICmdList, const FRHITextureCreateDesc& InCreateDesc, const DataType& DummyData)
{
FResourceBulkDataArrayView InitData(&DummyData, sizeof(DataType));
FRHITextureCreateDesc CreateDesc(InCreateDesc);
CreateDesc.AddFlags(ETextureCreateFlags::ShaderResource);
CreateDesc.SetClassName(SystemTexturesName);
CreateDesc.SetInitActionBulkData(&InitData);
return RHICmdList.CreateTexture(CreateDesc);
}
static TRefCountPtr<IPooledRenderTarget> CreateTexture(FRHICommandListImmediate& RHICmdList, TObjectPtr<class UTexture2D>& InCPUTexture, EPixelFormat InFormat, const TCHAR* InName)
{
check(InCPUTexture->Availability == ETextureAvailability::CPU);
FSharedImageConstRef Data = InCPUTexture->GetCPUCopy();
check(Data && Data->Format == ERawImageFormat::RGBA16F);
const TArrayView64<const FFloat16Color> DataView = Data->AsRGBA16F();
const FIntPoint DataSize(Data->SizeX, Data->SizeY);
const FRHITextureCreateDesc Desc =
FRHITextureCreateDesc::Create2D(InName, DataSize.X, DataSize.Y, InFormat)
.SetFlags(ETextureCreateFlags::ShaderResource | ETextureCreateFlags::FastVRAM)
.SetClassName(SystemTexturesName)
.SetInitActionInitializer();
FRHITextureInitializer Initializer = RHICmdList.CreateTextureInitializer(Desc);
check(InFormat == PF_FloatRGBA || InFormat == PF_G16R16F);
const uint32 ComponentCount = InFormat == PF_G16R16F ? 2u: 4u;
// Write the contents of the texture.
FRHITextureSubresourceInitializer Subresource = Initializer.GetTexture2DSubresource(0);
uint8* DestBuffer = reinterpret_cast<uint8*>(Subresource.Data);
for (int32 y = 0; y < DataSize.Y; ++y)
{
for (int32 x = 0; x < DataSize.X; ++x)
{
uint16* Dest = (uint16*)(DestBuffer + x * ComponentCount * sizeof(uint16) + y * Subresource.Stride);
const FFloat16Color Value = DataView[x + y * DataSize.X];
Dest[0] = Value.R.Encoded;
if (ComponentCount > 1) { Dest[1] = Value.G.Encoded; }
if (ComponentCount > 2) { Dest[2] = Value.B.Encoded; }
if (ComponentCount > 3) { Dest[3] = Value.A.Encoded; }
}
}
FTextureRHIRef Texture = Initializer.Finalize();
// Release CPU data which are no longer needed
#if !WITH_EDITORONLY_DATA
InCPUTexture->RemoveFromRoot();
InCPUTexture = nullptr;
#endif
return CreateRenderTarget(Texture, Desc.DebugName);
}
static bool IsTextureDataValid(UTexture2D* In)
{
return In && In->GetPlatformData() && In->GetCPUCopy();
}
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
{
WhiteDummy = CreateRenderTarget(GWhiteTexture->TextureRHI, TEXT("WhiteDummy"));
WhiteDummySRV = RHICmdList.CreateShaderResourceView((FRHITexture*)WhiteDummy->GetRHI(),FRHIViewDesc::CreateTextureSRV().SetDimensionFromTexture(WhiteDummy->GetRHI()));
}
// Create a BlackDummy texture
{
const FRHITextureCreateDesc Desc =
FRHITextureCreateDesc::Create2D(TEXT("BlackDummy"), 1, 1, PF_B8G8R8A8);
FTextureRHIRef Texture = CreateDummyTexture<FColor>(RHICmdList, Desc, FColor(0, 0, 0, 0));
BlackDummy = CreateRenderTarget(Texture, Desc.DebugName);
}
// Create a texture array that has a single black dummy slice
{
const FRHITextureCreateDesc Desc =
FRHITextureCreateDesc::Create2DArray(TEXT("BlackArrayDummy"), 1, 1, 1, PF_B8G8R8A8);
FTextureRHIRef Texture = CreateDummyTexture<FColor>(RHICmdList, Desc, FColor(0, 0, 0, 0));
BlackArrayDummy = CreateRenderTarget(Texture, Desc.DebugName);
}
// Create a texture that is a single UInt32 value set to 0
{
const FRHITextureCreateDesc Desc =
FRHITextureCreateDesc::Create2D(TEXT("ZeroUIntDummy"), 1, 1, PF_R32_UINT);
FTextureRHIRef Texture = CreateDummyTexture<uint32>(RHICmdList, Desc, 0u);
ZeroUIntDummy = CreateRenderTarget(Texture, Desc.DebugName);
}
// Create a texture array that is a single UInt32 value set to 0
{
const FRHITextureCreateDesc Desc =
FRHITextureCreateDesc::Create2DArray(TEXT("ZeroUIntArrayDummy"), 1, 1, 1, PF_R32_UINT);
FTextureRHIRef Texture = CreateDummyTexture<uint32>(RHICmdList, Desc, 0u);
ZeroUIntArrayDummy = CreateRenderTarget(Texture, Desc.DebugName);
}
// Create a BlackAlphaOneDummy texture
{
const FRHITextureCreateDesc Desc =
FRHITextureCreateDesc::Create2D(TEXT("BlackAlphaOneDummy"), 1, 1, PF_B8G8R8A8);
FTextureRHIRef Texture = CreateDummyTexture<FColor>(RHICmdList, Desc, FColor(0, 0, 0, 255));
BlackAlphaOneDummy = CreateRenderTarget(Texture, Desc.DebugName);
}
// Create a GreenDummy texture
{
const FRHITextureCreateDesc Desc =
FRHITextureCreateDesc::Create2DArray(TEXT("GreenDummy"), 1, 1, PF_B8G8R8A8)
.SetFlags(ETextureCreateFlags::ShaderResource)
.SetClassName(SystemTexturesName);
FTextureRHIRef Texture = CreateDummyTexture<FColor>(RHICmdList, Desc, FColor(0, 255, 0, 255));
GreenDummy = CreateRenderTarget(Texture, Desc.DebugName);
}
// Create a DefaultNormal8Bit texture
{
const FRHITextureCreateDesc Desc =
FRHITextureCreateDesc::Create2D(TEXT("DefaultNormal8Bit"), 1, 1, PF_B8G8R8A8);
FTextureRHIRef Texture = CreateDummyTexture<FColor>(RHICmdList, Desc, FColor(128, 128, 128, 255));
DefaultNormal8Bit = CreateRenderTarget(Texture, Desc.DebugName);
}
// Create the PerlinNoiseGradient texture
{
const uint32 Extent = 128;
const FRHITextureCreateDesc Desc =
FRHITextureCreateDesc::Create2D(TEXT("PerlinNoiseGradient"), Extent, Extent, PF_B8G8R8A8)
.SetFlags(ETextureCreateFlags::ShaderResource)
.SetClassName(SystemTexturesName)
.SetInitActionInitializer();
FRHITextureInitializer Initializer = RHICmdList.CreateTextureInitializer(Desc);
// Write the contents of the texture.
FRHITextureSubresourceInitializer Subresource = Initializer.GetTexture2DSubresource(0);
uint8* DestBuffer = reinterpret_cast<uint8*>(Subresource.Data);
// 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 < Extent; ++y)
{
for (int32 x = 0; x < Extent; ++x)
{
uint32* Dest = (uint32*)(DestBuffer + x * sizeof(uint32) + y * Subresource.Stride);
// pick a random direction (hacky way to overcome the quality issues FRandomStream has)
*Dest = gradtable[(uint32)(RandomStream.GetFraction() * 11.9999999f)];
}
}
FTextureRHIRef Texture = Initializer.Finalize();
PerlinNoiseGradient = CreateRenderTarget(Texture, Desc.DebugName);
}
if (GPixelFormats[PF_FloatRGBA].Supported)
{
const FRHITextureCreateDesc Desc =
FRHITextureCreateDesc::Create2D(TEXT("MaxFP16Depth"), 1, 1, PF_FloatRGBA);
FTextureRHIRef Texture = CreateDummyTexture<FFloat16Color>(RHICmdList, Desc, FFloat16Color(FLinearColor(65500.0f, 65500.0f, 65500.0f, 65500.0f)));
MaxFP16Depth = CreateRenderTarget(Texture, Desc.DebugName);
}
{
DepthDummy = static_cast<bool>(ERHIZBuffer::IsInverted) ? BlackDummy : WhiteDummy;
BlackDepthCube = CreateRenderTarget(GBlackTextureDepthCube->TextureRHI, TEXT("BlackDepthCube"));
}
// Create a dummy stencil SRV.
{
const FRHITextureCreateDesc Desc =
FRHITextureCreateDesc::Create2D(TEXT("StencilDummy"), 1, 1, PF_R8G8B8A8_UINT);
FTextureRHIRef Texture = CreateDummyTexture<FColor>(RHICmdList, Desc, FColor::White);
StencilDummy = CreateRenderTarget(Texture, Desc.DebugName);
StencilDummySRV = RHICmdList.CreateShaderResourceView(StencilDummy->GetRHI(), FRHIViewDesc::CreateTextureSRV().SetDimensionFromTexture(Texture));
}
if (GPixelFormats[PF_FloatRGBA].Supported)
{
const FRHITextureCreateDesc Desc =
FRHITextureCreateDesc::Create2D(TEXT("MidGreyDummy"), 1, 1, PF_FloatRGBA);
FTextureRHIRef Texture = CreateDummyTexture<FFloat16Color>(RHICmdList, Desc, FFloat16Color(FLinearColor(0.5f, 0.5f, 0.5f, 0.5f)));
MidGreyDummy = CreateRenderTarget(Texture, Desc.DebugName);
}
// Create a VolumetricBlackDummy texture
{
VolumetricBlackDummy = CreateRenderTarget(GBlackVolumeTexture->TextureRHI, TEXT("VolumetricBlackDummy"));
VolumetricBlackAlphaOneDummy = CreateRenderTarget(GBlackAlpha1VolumeTexture->TextureRHI, TEXT("VolumetricBlackAlphaOneDummy"));
VolumetricBlackUintDummy = CreateRenderTarget(GBlackUintVolumeTexture->TextureRHI, TEXT("VolumetricBlackUintDummy"));
}
// Create Cube BlackDummy textures
{
CubeBlackDummy = CreateRenderTarget(GBlackTextureCube->TextureRHI, TEXT("CubeBlackDummy"));
CubeArrayBlackDummy = CreateRenderTarget(GBlackCubeArrayTexture->TextureRHI, TEXT("CubeArrayBlackDummy"));
}
}
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 && GPixelFormats[PF_R16_UINT].Supported)
{
const FRHITextureCreateDesc Desc =
FRHITextureCreateDesc::Create2D(TEXT("SobolSampling"), 32, 16, PF_R16_UINT)
.SetFlags(ETextureCreateFlags::ShaderResource)
.SetClassName(SystemTexturesName)
.SetInitActionInitializer();
FRHITextureInitializer Initializer = RHICmdList.CreateTextureInitializer(Desc);
// Write the contents of the texture.
FRHITextureSubresourceInitializer Subresource = Initializer.GetTexture2DSubresource(0);
uint8* DestBuffer = reinterpret_cast<uint8*>(Subresource.Data);
for (int y = 0; y < 16; ++y)
{
uint16* Dest = (uint16*)(DestBuffer + y * Subresource.Stride);
// 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);
}
}
FTextureRHIRef Texture = Initializer.Finalize();
SobolSampling = CreateRenderTarget(Texture, Desc.DebugName);
}
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));
GRenderTargetPool.FindFreeElement(RHICmdList, Desc, HairLUT0, TEXT("HairLUT0"));
// 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(HairLUT0->GetRHI(), 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->GetRHI(), ERHIAccess::Unknown, ERHIAccess::SRVMask));
HairLUT1 = HairLUT0;
HairLUT2 = HairLUT0;
}
// ASCII texture
{
EPixelFormat Format = PF_R8;
const uint32 BytesPerPixel = 1;
const uint32 CharacterCount = 96u;
const uint32 CharacterRes = 8u;
const uint32 CharacterStride= CharacterRes * BytesPerPixel;
FPooledRenderTargetDesc Desc(FPooledRenderTargetDesc::Create2DDesc(FIntPoint(CharacterCount * CharacterRes, CharacterRes), Format, FClearValueBinding::None, TexCreate_None, TexCreate_ShaderResource, false));
const uint32 Characters[] =
{
0x00000000,0x00000000,0x0c1e1e0c,0x000c000c,0x00363636,0x00000000,0x367f3636,0x0036367f,
0x1e033e0c,0x000c1f30,0x18336300,0x0063660c,0x6e1c361c,0x006e333b,0x00030606,0x00000000,
0x06060c18,0x00180c06,0x18180c06,0x00060c18,0xff3c6600,0x0000663c,0x3f0c0c00,0x00000c0c,
0x00000000,0x060c0e00,0x3f000000,0x00000000,0x00000000,0x000c0c00,0x0c183060,0x00010306,
0x3f3b331e,0x001e3337,0x0c0c0f0c,0x003f0c0c,0x1c30331e,0x003f3306,0x1c30331e,0x001e3330,
0x33363c38,0x0030307f,0x301f033f,0x001e3330,0x1f03061c,0x001e3333,0x1830333f,0x0006060c,
0x1e33331e,0x001e3333,0x3e33331e,0x000e1830,0x0c0c0000,0x000c0c00,0x0c0c0000,0x060c0e00,
0x03060c18,0x00180c06,0x003f0000,0x0000003f,0x30180c06,0x00060c18,0x1830331e,0x000c000c,
0x7b7b633e,0x001e037b,0x33331e0c,0x0033333f,0x3e66663f,0x003f6666,0x0303663c,0x003c6603,
0x6666363f,0x003f3666,0x1e16467f,0x007f4616,0x1e16467f,0x000f0616,0x0303663c,0x007c6673,
0x3f333333,0x00333333,0x0c0c0c1e,0x001e0c0c,0x30303078,0x001e3333,0x1e366667,0x00676636,
0x0606060f,0x007f6646,0x6b7f7763,0x00636363,0x7b6f6763,0x00636373,0x6363361c,0x001c3663,
0x3e66663f,0x000f0606,0x3333331e,0x00381e3b,0x3e66663f,0x0067361e,0x1c07331e,0x001e3338,
0x0c0c2d3f,0x001e0c0c,0x33333333,0x003f3333,0x33333333,0x000c1e33,0x6b636363,0x0063777f,
0x1c366363,0x00636336,0x1e333333,0x001e0c0c,0x0c19337f,0x007f6346,0x0606061e,0x001e0606,
0x180c0603,0x00406030,0x1818181e,0x001e1818,0x63361c08,0x00000000,0x00000000,0xff000000,
0x00180c0c,0x00000000,0x301e0000,0x006e333e,0x663e0607,0x003d6666,0x331e0000,0x001e3303,
0x3e303038,0x006e3333,0x331e0000,0x001e033f,0x0f06361c,0x000f0606,0x336e0000,0x1f303e33,
0x6e360607,0x00676666,0x0c0e000c,0x001e0c0c,0x181e0018,0x0e1b1818,0x36660607,0x0067361e,
0x0c0c0c0e,0x001e0c0c,0x7f370000,0x0063636b,0x331f0000,0x00333333,0x331e0000,0x001e3333,
0x663b0000,0x0f063e66,0x336e0000,0x78303e33,0x361b0000,0x000f0636,0x033e0000,0x001f301e,
0x0c3e0c08,0x00182c0c,0x33330000,0x006e3333,0x33330000,0x000c1e33,0x63630000,0x00367f6b,
0x36630000,0x0063361c,0x33330000,0x1f303e33,0x193f0000,0x003f260c,0x070c0c38,0x00380c0c,
0x00181818,0x00181818,0x380c0c07,0x00070c0c,0x00003b6e,0x00000000,0x00000000,0x00000000
};
GRenderTargetPool.FindFreeElement(RHICmdList, Desc, AsciiTexture, TEXT("AsciiTexture"));
uint32 DestStride;
uint8* DestBuffer = (uint8*)RHICmdList.LockTexture2D(AsciiTexture->GetRHI(), 0, RLM_WriteOnly, DestStride, false);
for (int32 c = 0; c < CharacterCount; c++)
{
const uint32 Hi = Characters[c * 2];
const uint32 Lo = Characters[c * 2 + 1];
for (int32 y = 0; y < CharacterRes; y++)
for (int32 x = 0; x < CharacterRes; x++)
{
uint8* Dest = (uint8*)(DestBuffer + x * BytesPerPixel + (c * CharacterStride) + y * DestStride);
const uint32 C = y < CharacterRes/2 ? Hi : Lo;
const bool bFilled = (C & (1u << ((y * CharacterRes + x) & 31u)));
Dest[0] = bFilled ? 0xFF : 0x0;
}
}
RHICmdList.UnlockTexture2D(AsciiTexture->GetRHI(), 0, false);
}
// The PreintegratedGF maybe used on forward shading including mobile platform, initialize 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 && UE::PixelFormat::HasCapabilities(PF_G16R16, EPixelFormatCapabilities::TextureFilterable))
{
Format = PF_G16R16;
}
FIntPoint Extent(128, 32);
if (bReference)
{
Extent.X = 128;
Extent.Y = 128;
}
const FRHITextureCreateDesc Desc =
FRHITextureCreateDesc::Create2D(TEXT("PreintegratedGF"), Extent, Format)
.SetFlags(ETextureCreateFlags::ShaderResource)
.SetClassName(SystemTexturesName)
.SetInitActionInitializer();
FRHITextureInitializer Initializer = RHICmdList.CreateTextureInitializer(Desc);
// Write the contents of the texture.
FRHITextureSubresourceInitializer Subresource = Initializer.GetTexture2DSubresource(0);
uint8* DestBuffer = reinterpret_cast<uint8*>(Subresource.Data);
// x is NoV, y is roughness
for (int32 y = 0; y < Extent.Y; y++)
{
float Roughness = (float)(y + 0.5f) / Extent.Y;
float m = Roughness * Roughness;
float m2 = m * m;
for (int32 x = 0; x < Extent.X; x++)
{
float NoV = (float)(x + 0.5f) / Extent.X;
FVector3f 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);
FVector3f H(SinTheta * FMath::Cos(Phi), SinTheta * FMath::Sin(Phi), CosTheta);
FVector3f 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);
FVector3f L(SinTheta * FMath::Cos(Phi), SinTheta * FMath::Sin(Phi), CosTheta);
FVector3f 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 (Format == PF_A16B16G16R16)
{
uint16* Dest = (uint16*)(DestBuffer + x * 8 + y * Subresource.Stride);
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 (Format == PF_G16R16)
{
uint16* Dest = (uint16*)(DestBuffer + x * 4 + y * Subresource.Stride);
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(Format == PF_R8G8);
uint8* Dest = (uint8*)(DestBuffer + x * 2 + y * Subresource.Stride);
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);
}
}
}
FTextureRHIRef Texture = Initializer.Finalize();
PreintegratedGF = CreateRenderTarget(Texture, Desc.DebugName);
}
{
// Create the PerlinNoise3D texture (similar to http://prettyprocs.wordpress.com/2012/10/20/fast-perlin-noise/)
{
const uint32 Extent = 16;
const uint32 Square = Extent * Extent;
const FRHITextureCreateDesc Desc =
FRHITextureCreateDesc::Create3D(TEXT("PerlinNoise3D"), Extent, Extent, Extent, PF_B8G8R8A8)
.SetFlags(ETextureCreateFlags::ShaderResource | ETextureCreateFlags::NoTiling)
.SetClassName(SystemTexturesName);
FTextureRHIRef Texture3D = RHICreateTexture(Desc);
// 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 < Extent; ++z)
{
for (uint32 y = 0; y < Extent; ++y)
{
for (uint32 x = 0; x < Extent; ++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, Extent, Extent, Extent);
RHICmdList.UpdateTexture3D(
Texture3D,
0,
Region,
Extent * sizeof(uint32),
Extent * Extent * sizeof(uint32),
(const uint8*)DestBuffer.GetData());
PerlinNoise3D = CreateRenderTarget(Texture3D, Desc.DebugName);
} // end Create the PerlinNoise3D texture
}
// 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 && 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);
}
{
const uint32 Extent = 64;
const FRHITextureCreateDesc Desc =
FRHITextureCreateDesc::Create2D(TEXT("SSAORandomization"), Extent, Extent, PF_R8G8)
.SetFlags(ETextureCreateFlags::ShaderResource)
.SetClassName(SystemTexturesName)
.SetInitActionInitializer();
FRHITextureInitializer Initializer = RHICmdList.CreateTextureInitializer(Desc);
// Write the contents of the texture.
FRHITextureSubresourceInitializer Subresource = Initializer.GetTexture2DSubresource(0);
uint8* DestBuffer = (uint8*)Subresource.Data;
for (int32 y = 0; y < Extent; ++y)
{
for (int32 x = 0; x < Extent; ++x)
{
uint8* Dest = (uint8*)(DestBuffer + x * sizeof(uint16) + y * Subresource.Stride);
uint32 Index = (x % 4) + (y % 4) * 4;
Dest[0] = Bases[Index].R;
Dest[1] = Bases[Index].G;
}
}
FTextureRHIRef Texture = Initializer.Finalize();
SSAORandomization = CreateRenderTarget(Texture, Desc.DebugName);
}
}
}
static const auto MobileGTAOPreIntegratedTextureTypeCVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Mobile.GTAOPreIntegratedTextureType"));
if (CurrentFeatureLevel < 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;
const FRHITextureCreateDesc Desc =
FRHITextureCreateDesc(TEXT("GTAOPreIntegrated"), bGTAOPreIngegratedUsingVolumeLUT ? ETextureDimension::Texture3D : ETextureDimension::Texture2D)
.SetExtent(Extent)
.SetDepth(bGTAOPreIngegratedUsingVolumeLUT ? Extent : 1)
.SetFormat(PF_R16F)
.SetFlags(ETextureCreateFlags::ShaderResource)
.SetClassName(SystemTexturesName)
.SetInitAction(bGTAOPreIngegratedUsingVolumeLUT ? ERHITextureInitAction::Default : ERHITextureInitAction::Initializer);
FRHITextureInitializer Initializer = RHICmdList.CreateTextureInitializer(Desc);
// Write the contents of the texture.
TArray<FFloat16> TempBuffer;
TempBuffer.AddZeroed(Extent * Extent * Extent);
FFloat16* DestBuffer = nullptr;
if (bGTAOPreIngegratedUsingVolumeLUT)
{
DestBuffer = TempBuffer.GetData();
}
else
{
FRHITextureSubresourceInitializer Subresource = Initializer.GetTexture2DSubresource(0);
DestBuffer = (FFloat16*)Subresource.Data;
}
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;
}
}
}
TRefCountPtr<FRHITexture> Texture = Initializer.Finalize();
if (bGTAOPreIngegratedUsingVolumeLUT)
{
FUpdateTextureRegion3D Region(0, 0, 0, 0, 0, 0, Extent, Extent, Extent);
RHICmdList.UpdateTexture3D(
(FRHITexture*)Texture.GetReference(),
0,
Region,
Extent * sizeof(FFloat16),
Extent * Extent * sizeof(FFloat16),
(const uint8*)DestBuffer);
}
GTAOPreIntegrated = CreateRenderTarget(Texture, Desc.DebugName);
}
// Create a texture array that is a single UInt32 value set to 0, this texture is AtomicCompatible on SM6
{
FRHITextureCreateDesc Desc =
FRHITextureCreateDesc::Create2DArray(TEXT("ZeroUIntArrayAtomicCompatDummy"), 1, 1, 1, PF_R32_UINT)
.SetFlags(ETextureCreateFlags::ShaderResource);
if (InFeatureLevel >= ERHIFeatureLevel::SM6)
{
Desc.AddFlags(ETextureCreateFlags::AtomicCompatible);
}
FTextureRHIRef Texture = CreateDummyTexture<uint32>(RHICmdList, Desc, 0u);
ZeroUIntArrayAtomicCompatDummy = CreateRenderTarget(Texture, Desc.DebugName);
}
#if EXPORT_SYSTEM_TEXTURES
ExportSystemTextures();
#endif
// Initialize textures only once.
FeatureLevelInitializedTo = InFeatureLevel;
}
void FSystemTextures::InitializeEngineDependentTextures(FRHICommandListImmediate& RHICmdList)
{
if (GEngine)
{
// LTC Textures (used by Rect Lights and/or BSDF evaluation)
// GGX - LTC matrix coefficients (4-coefficients)
if (GGXLTCMat == nullptr && IsTextureDataValid(GEngine->GGXLTCMatTexture))
{
GGXLTCMat = CreateTexture(RHICmdList, GEngine->GGXLTCMatTexture, PF_FloatRGBA, TEXT("GGX.LTCMat"));
}
// GGX - Split-Sum Amplitude coefficients (2-components)
if (GGXLTCAmp == nullptr && IsTextureDataValid(GEngine->GGXLTCAmpTexture))
{
GGXLTCAmp = CreateTexture(RHICmdList, GEngine->GGXLTCAmpTexture, PF_G16R16F, TEXT("GGX.LTCAmp"));
}
// Sheen - Matrix & directional albedo (3-components)
if (SheenLTC == nullptr && IsTextureDataValid(GEngine->SheenLTCTexture))
{
SheenLTC = CreateTexture(RHICmdList, GEngine->SheenLTCTexture, PF_FloatRGBA, TEXT("Sheen.LTC"));
}
}
// Initialize textures only once (SheenLTC is only used with Substrate)
bEngineDependentTexturesInitialized = GGXLTCMat && GGXLTCAmp && (!Substrate::IsSubstrateEnabled() || SheenLTC);
}
void FSystemTextures::ReleaseRHI()
{
WhiteDummySRV.SafeRelease();
WhiteDummy.SafeRelease();
BlackDummy.SafeRelease();
BlackArrayDummy.SafeRelease();
BlackAlphaOneDummy.SafeRelease();
BlackArrayDummy.SafeRelease();
PerlinNoiseGradient.SafeRelease();
PerlinNoise3D.SafeRelease();
SobolSampling.SafeRelease();
SSAORandomization.SafeRelease();
GTAOPreIntegrated.SafeRelease();
PreintegratedGF.SafeRelease();
HairLUT0.SafeRelease();
HairLUT1.SafeRelease();
HairLUT2.SafeRelease();
GGXLTCMat.SafeRelease();
GGXLTCAmp.SafeRelease();
SheenLTC.SafeRelease();
MaxFP16Depth.SafeRelease();
DepthDummy.SafeRelease();
GreenDummy.SafeRelease();
DefaultNormal8Bit.SafeRelease();
VolumetricBlackDummy.SafeRelease();
VolumetricBlackAlphaOneDummy.SafeRelease();
VolumetricBlackUintDummy.SafeRelease();
CubeBlackDummy.SafeRelease();
CubeArrayBlackDummy.SafeRelease();
ZeroUIntDummy.SafeRelease();
ZeroUIntArrayDummy.SafeRelease();
ZeroUIntArrayAtomicCompatDummy.SafeRelease();
MidGreyDummy.SafeRelease();
StencilDummy.SafeRelease();
StencilDummySRV.SafeRelease();
BlackDepthCube.SafeRelease();
GTAOPreIntegrated.SafeRelease();
AsciiTexture.SafeRelease();
DefaultTextures.Empty();
DefaultBuffers.Empty();
HashDefaultTextures.Clear();
HashDefaultBuffers.Clear();
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"), ERDGTextureFlags::SkipTracking);
}
FRDGTextureRef FSystemTextures::GetBlackAlphaOneDummy(FRDGBuilder& GraphBuilder) const
{
return GraphBuilder.RegisterExternalTexture(BlackAlphaOneDummy, TEXT("BlackAlphaOneDummy"), ERDGTextureFlags::SkipTracking);
}
FRDGTextureRef FSystemTextures::GetBlackArrayDummy(FRDGBuilder& GraphBuilder) const
{
return GraphBuilder.RegisterExternalTexture(BlackArrayDummy, TEXT("BlackArrayDummy"), ERDGTextureFlags::SkipTracking);
}
FRDGTextureRef FSystemTextures::GetWhiteDummy(FRDGBuilder& GraphBuilder) const
{
return GraphBuilder.RegisterExternalTexture(WhiteDummy, TEXT("WhiteDummy"), ERDGTextureFlags::SkipTracking);
}
FRDGTextureRef FSystemTextures::GetMaxFP16Depth(FRDGBuilder& GraphBuilder) const
{
return GraphBuilder.RegisterExternalTexture(MaxFP16Depth, TEXT("MaxFP16Depth"), ERDGTextureFlags::SkipTracking);
}
FRDGTextureRef FSystemTextures::GetDepthDummy(FRDGBuilder& GraphBuilder) const
{
return GraphBuilder.RegisterExternalTexture(DepthDummy, TEXT("DepthDummy"), ERDGTextureFlags::SkipTracking);
}
FRDGTextureRef FSystemTextures::GetStencilDummy(FRDGBuilder& GraphBuilder) const
{
return GraphBuilder.RegisterExternalTexture(StencilDummy, TEXT("StencilDummy"), ERDGTextureFlags::SkipTracking);
}
FRDGTextureRef FSystemTextures::GetGreenDummy(FRDGBuilder& GraphBuilder) const
{
return GraphBuilder.RegisterExternalTexture(GreenDummy, TEXT("GreenDummy"), ERDGTextureFlags::SkipTracking);
}
FRDGTextureRef FSystemTextures::GetDefaultNormal8Bit(FRDGBuilder& GraphBuilder) const
{
return GraphBuilder.RegisterExternalTexture(DefaultNormal8Bit, TEXT("DefaultNormal8Bit"), ERDGTextureFlags::SkipTracking);
}
FRDGTextureRef FSystemTextures::GetMidGreyDummy(FRDGBuilder& GraphBuilder) const
{
return GraphBuilder.RegisterExternalTexture(MidGreyDummy, TEXT("MidGreyDummy"), ERDGTextureFlags::SkipTracking);
}
FRDGTextureRef FSystemTextures::GetVolumetricBlackDummy(FRDGBuilder& GraphBuilder) const
{
return GraphBuilder.RegisterExternalTexture(VolumetricBlackDummy, TEXT("VolumetricBlackDummy"), ERDGTextureFlags::SkipTracking);
}
FRDGTextureRef FSystemTextures::GetVolumetricBlackUintDummy(FRDGBuilder& GraphBuilder) const
{
return GraphBuilder.RegisterExternalTexture(VolumetricBlackUintDummy, TEXT("VolumetricBlackUintDummy"), ERDGTextureFlags::SkipTracking);
}
FRDGTextureRef FSystemTextures::GetCubeBlackDummy(FRDGBuilder& GraphBuilder) const
{
return GraphBuilder.RegisterExternalTexture(CubeBlackDummy, TEXT("CubeBlackDummy"), ERDGTextureFlags::SkipTracking);
}
FRDGTextureRef FSystemTextures::GetCubeArrayBlackDummy(FRDGBuilder& GraphBuilder) const
{
return GraphBuilder.RegisterExternalTexture(CubeArrayBlackDummy, TEXT("CubeArrayBlackDummy"), ERDGTextureFlags::SkipTracking);
}
FRDGTextureRef FSystemTextures::GetZeroUIntDummy(FRDGBuilder& GraphBuilder) const
{
return GraphBuilder.RegisterExternalTexture(ZeroUIntDummy, TEXT("ZeroUIntDummy"), ERDGTextureFlags::SkipTracking);
}
FRDGTextureRef FSystemTextures::GetZeroUIntArrayDummy(FRDGBuilder& GraphBuilder) const
{
return GraphBuilder.RegisterExternalTexture(ZeroUIntArrayDummy, TEXT("ZeroUIntArrayDummy"), ERDGTextureFlags::SkipTracking);
}
FRDGTextureRef FSystemTextures::GetZeroUIntArrayAtomicCompatDummy(FRDGBuilder& GraphBuilder) const
{
return GraphBuilder.RegisterExternalTexture(ZeroUIntArrayAtomicCompatDummy, TEXT("ZeroUIntArrayAtomicCompatDummy"), ERDGTextureFlags::SkipTracking);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// Default textures
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(ETextureDimension Dimension, 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;
Out.Dimension = Dimension;
return Out;
}
// Convert from X to 4 components data float/uint/int. Supported input are:
// * float
// * int32
// * uint32
// * FVector2D
// * FIntPoint
// * FVector3f
// * FVector4f
// * FUintVector4
// * FClearValueBinding
FIntVector4 ToVector(int32 Value) { return FIntVector4(Value, Value, Value, Value); }
FVector4f ToVector(float Value) { return FVector4f(Value, Value, Value, Value); }
FUintVector4 ToVector(uint32 Value) { return FUintVector4(Value, Value, Value, Value); }
FVector4f ToVector(const FVector3f & Value) { return FVector4f(Value.X, Value.Y, Value.Z, 0); }
FVector4f ToVector(const FVector4f & Value) { return Value; }
FVector4f ToVector(const FVector2D& Value) { return FVector4f(Value.X, Value.Y, 0, 0); }
FIntVector4 ToVector(const FIntPoint& Value) { return FIntVector4(Value.X, Value.Y, 0, 0); }
FUintVector4 ToVector(const FUintVector4& Value) { return Value; }
FVector4f ToVector(const FClearValueBinding & Value) { return FVector4f(Value.Value.Color[0], Value.Value.Color[1], Value.Value.Color[2], Value.Value.Color[3]); }
template <typename TInputType> struct TFormatConversionTraits { /*Error*/ };
template <> struct TFormatConversionTraits<FVector4f> { typedef float Type; };
template <> struct TFormatConversionTraits<FUintVector4>{ typedef uint32 Type; };
template <> struct TFormatConversionTraits<FIntVector4> { typedef int32 Type; };
enum class EDefaultInputType
{
Typed,
UNorm,
SNorm,
UNorm10,
UNorm11,
UNorm2,
UNorm5,
UNorm1
};
// Convert input type into the final type. This function manages UNorm/SNorm type by assuming if the input if float, its value is normalized in [0..1].
template <typename TInType, typename TOutType, EDefaultInputType InputFormatType>
TOutType ConvertInputFormat(const TInType& In)
{
return TOutType(In);
}
template<> uint64 ConvertInputFormat<float, uint64, EDefaultInputType::UNorm>(const float& In) { return FMath::Clamp(In, 0.f, 1.f) * float(MAX_uint64); }
template<> int64 ConvertInputFormat<float, int64, EDefaultInputType::SNorm>(const float& In) { return FMath::Clamp(In, -1.f, 1.f) * float(MAX_int64); }
template<> uint32 ConvertInputFormat<float, uint32, EDefaultInputType::UNorm>(const float& In) { return FMath::Clamp(In, 0.f, 1.f) * float(MAX_uint32); }
template<> int32 ConvertInputFormat<float, int32, EDefaultInputType::SNorm>(const float& In) { return FMath::Clamp(In, -1.f, 1.f) * float(MAX_int32); }
template<> uint16 ConvertInputFormat<float, uint16, EDefaultInputType::UNorm>(const float& In) { return FMath::Clamp(In, 0.f, 1.f) * MAX_uint16; }
template<> int16 ConvertInputFormat<float, int16, EDefaultInputType::SNorm>(const float& In) { return FMath::Clamp(In, -1.f, 1.f) * MAX_int16; }
template<> uint8 ConvertInputFormat<float, uint8, EDefaultInputType::UNorm>(const float& In) { return FMath::Clamp(In, 0.f, 1.f) * MAX_uint8; }
template<> int8 ConvertInputFormat<float, int8, EDefaultInputType::SNorm>(const float& In) { return FMath::Clamp(In, -1.f, 1.f) * MAX_int8; }
template<> uint32 ConvertInputFormat<float, uint32, EDefaultInputType::UNorm10>(const float& In) { return FMath::Clamp(In, 0.f, 1.f) * 1024u; }
template<> uint32 ConvertInputFormat<float, uint32, EDefaultInputType::UNorm11>(const float& In) { return FMath::Clamp(In, 0.f, 1.f) * 2048u; }
template<> uint32 ConvertInputFormat<float, uint32, EDefaultInputType::UNorm2> (const float& In) { return FMath::Clamp(In, 0.f, 1.f) * 3u; }
template<> uint32 ConvertInputFormat<float, uint32, EDefaultInputType::UNorm1> (const float& In) { return uint32(In > 0.5f);}
template<> uint32 ConvertInputFormat<float, uint32, EDefaultInputType::UNorm5>(const float& In) { return FMath::Clamp(In, 0.f, 1.f) * 31u; }
// 4 components conversion with swizzling
template <EDefaultInputType InputFormatType, typename TInType, typename TOutType, uint32 SwizzleX, uint32 SwizzleY, uint32 SwizzleZ, uint32 SwizzleW>
void FormatData(const TInType& In, uint8* Out, uint32& OutByteCount)
{
TOutType* OutTyped = (TOutType*)Out;
OutTyped[0] = ConvertInputFormat<typename TFormatConversionTraits<TInType>::Type, TOutType, InputFormatType>(In[SwizzleX]);
OutTyped[1] = ConvertInputFormat<typename TFormatConversionTraits<TInType>::Type, TOutType, InputFormatType>(In[SwizzleY]);
OutTyped[2] = ConvertInputFormat<typename TFormatConversionTraits<TInType>::Type, TOutType, InputFormatType>(In[SwizzleZ]);
OutTyped[3] = ConvertInputFormat<typename TFormatConversionTraits<TInType>::Type, TOutType, InputFormatType>(In[SwizzleW]);
OutByteCount = 4 * sizeof(TOutType);
}
// 3 components conversion with swizzling
template <EDefaultInputType InputFormatType, typename TInType, typename TOutType, uint32 SwizzleX, uint32 SwizzleY, uint32 SwizzleZ>
void FormatData(const TInType& In, uint8* Out, uint32& OutByteCount)
{
TOutType* OutTyped = (TOutType*)Out;
OutTyped[0] = ConvertInputFormat<typename TFormatConversionTraits<TInType>::Type, TOutType, InputFormatType>(In[SwizzleX]);
OutTyped[1] = ConvertInputFormat<typename TFormatConversionTraits<TInType>::Type, TOutType, InputFormatType>(In[SwizzleY]);
OutTyped[2] = ConvertInputFormat<typename TFormatConversionTraits<TInType>::Type, TOutType, InputFormatType>(In[SwizzleZ]);
OutByteCount = 3 * sizeof(TOutType);
}
// 2 components conversion with swizzling
template <EDefaultInputType InputFormatType, typename TInType, typename TOutType, uint32 SwizzleX, uint32 SwizzleY>
void FormatData(const TInType& In, uint8* Out, uint32& OutByteCount)
{
TOutType* OutTyped = (TOutType*)Out;
OutTyped[0] = ConvertInputFormat<typename TFormatConversionTraits<TInType>::Type, TOutType, InputFormatType>(In[SwizzleX]);
OutTyped[1] = ConvertInputFormat<typename TFormatConversionTraits<TInType>::Type, TOutType, InputFormatType>(In[SwizzleY]);
OutByteCount = 2 * sizeof(TOutType);
}
// 1 component conversion
template <EDefaultInputType InputFormatType, typename TInType, typename TOutType>
void FormatData(const TInType& In, uint8* Out, uint32& OutByteCount)
{
TOutType* OutTyped = (TOutType*)Out;
OutTyped[0] = ConvertInputFormat<typename TFormatConversionTraits<TInType>::Type, TOutType, InputFormatType>(In[0]);
OutByteCount = sizeof(TOutType);
}
template <typename TInType>
void FormatData111110(const TInType& In, uint8* Out, uint32& OutByteCount)
{
uint32* OutTyped = (uint32*)Out;
*OutTyped =
(2047u & ConvertInputFormat<typename TFormatConversionTraits<TInType>::Type, uint32, EDefaultInputType::UNorm11>(In[0])) |
((2047u & ConvertInputFormat<typename TFormatConversionTraits<TInType>::Type, uint32, EDefaultInputType::UNorm11>(In[1]))<<11)|
((1023u & ConvertInputFormat<typename TFormatConversionTraits<TInType>::Type, uint32, EDefaultInputType::UNorm10>(In[2]))<<22);
OutByteCount = 4;
}
template <typename TInType>
void FormatData1010102(const TInType& In, uint8* Out, uint32& OutByteCount)
{
uint32* OutTyped = (uint32*)Out;
*OutTyped =
(1023u & ConvertInputFormat<typename TFormatConversionTraits<TInType>::Type, uint32, EDefaultInputType::UNorm10>(In[0])) |
((1023u & ConvertInputFormat<typename TFormatConversionTraits<TInType>::Type, uint32, EDefaultInputType::UNorm10>(In[1])) << 10) |
((1023u & ConvertInputFormat<typename TFormatConversionTraits<TInType>::Type, uint32, EDefaultInputType::UNorm10>(In[2])) << 20) |
(( 3u & ConvertInputFormat<typename TFormatConversionTraits<TInType>::Type, uint32, EDefaultInputType::UNorm2> (In[3])) << 30);
OutByteCount = 4;
}
template<typename TInType>
void FormatData5551(const TInType& In, uint8* Out, uint32& OutByteCount)
{
uint16* OutTyped = (uint16*)Out;
*OutTyped =
(
((31u & ConvertInputFormat<typename TFormatConversionTraits<TInType>::Type, uint32, EDefaultInputType::UNorm5>(In[0])) << 1) |
((31u & ConvertInputFormat<typename TFormatConversionTraits<TInType>::Type, uint32, EDefaultInputType::UNorm5>(In[1])) << 6) |
((31u & ConvertInputFormat<typename TFormatConversionTraits<TInType>::Type, uint32, EDefaultInputType::UNorm5>(In[2])) << 11) |
((1u & ConvertInputFormat<typename TFormatConversionTraits<TInType>::Type, uint32, EDefaultInputType::UNorm1>(In[3]))));
OutByteCount = 2;
}
template<typename TInType>
void FormatData9995(const TInType& In, uint8* Out, uint32& OutByteCount)
{
FFloat3PackedSE PackedFloat(FLinearColor(In[0], In[1], In[2], 1.0f));
uint32* OutTyped = (uint32*)Out;
OutByteCount = 4;
*OutTyped = PackedFloat.EncodedValue;
}
template<typename TInType>
void InitializeData(const TInType& InData, EPixelFormat InFormat, uint8* OutData, uint32& OutByteCount)
{
// If a new format is added insure that it is either supported here, or at least flagged as not supported
static_assert(PF_MAX == 94);
switch (InFormat)
{
// 64bits
case PF_R64_UINT: { FormatData<EDefaultInputType::Typed, TInType, uint64> (InData, OutData, OutByteCount); } break;
// 32bits
case PF_R32G32B32A32_UINT: { FormatData<EDefaultInputType::Typed, TInType, uint32, 0, 1, 2, 3> (InData, OutData, OutByteCount); } break;
case PF_A32B32G32R32F: { FormatData<EDefaultInputType::Typed, TInType, float, 3, 2, 1, 0> (InData, OutData, OutByteCount); } break;
case PF_R32G32B32_UINT: { FormatData<EDefaultInputType::Typed, TInType, uint32, 0, 1, 2> (InData, OutData, OutByteCount); } break;
case PF_R32G32B32_SINT: { FormatData<EDefaultInputType::Typed, TInType, int32, 0, 1, 2> (InData, OutData, OutByteCount); } break;
case PF_R32G32B32F: { FormatData<EDefaultInputType::Typed, TInType, float, 0, 1, 2> (InData, OutData, OutByteCount); } break;
case PF_R32G32_UINT: { FormatData<EDefaultInputType::Typed, TInType, uint32, 0, 1> (InData, OutData, OutByteCount); } break;
case PF_G32R32F: { FormatData<EDefaultInputType::Typed, TInType, float, 1, 0> (InData, OutData, OutByteCount); } break;
case PF_R32_UINT: { FormatData<EDefaultInputType::Typed, TInType, uint32> (InData, OutData, OutByteCount); } break;
case PF_R32_SINT: { FormatData<EDefaultInputType::Typed, TInType, int32> (InData, OutData, OutByteCount); } break;
case PF_R32_FLOAT: { FormatData<EDefaultInputType::Typed, TInType, float> (InData, OutData, OutByteCount); } break;
// 16bits
case PF_R16G16B16A16_UINT: { FormatData<EDefaultInputType::Typed, TInType, uint16, 0, 1, 2, 3> (InData, OutData, OutByteCount); } break;
case PF_R16G16B16A16_SINT: { FormatData<EDefaultInputType::Typed, TInType, int16, 0, 1, 2, 3> (InData, OutData, OutByteCount); } break;
case PF_R16G16B16A16_UNORM: { FormatData<EDefaultInputType::UNorm, TInType, uint16, 0, 1, 2, 3> (InData, OutData, OutByteCount); } break;
case PF_R16G16B16A16_SNORM: { FormatData<EDefaultInputType::SNorm, TInType, int16, 0, 1, 2, 3> (InData, OutData, OutByteCount); } break;
case PF_A16B16G16R16: { FormatData<EDefaultInputType::UNorm, TInType, uint16, 3, 2, 1, 0> (InData, OutData, OutByteCount); } break;
case PF_FloatRGBA: { FormatData<EDefaultInputType::Typed, TInType, FFloat16, 0, 1, 2, 3> (InData, OutData, OutByteCount); } break;
case PF_R16G16_UINT: { FormatData<EDefaultInputType::Typed, TInType, uint16, 0, 1> (InData, OutData, OutByteCount); } break;
case PF_R16G16_SINT: { FormatData<EDefaultInputType::Typed, TInType, int16, 0, 1> (InData, OutData, OutByteCount); } break;
case PF_G16R16: { FormatData<EDefaultInputType::UNorm, TInType, uint16, 1, 0> (InData, OutData, OutByteCount); } break;
case PF_G16R16_SNORM: { FormatData<EDefaultInputType::SNorm, TInType, int16, 1, 0> (InData, OutData, OutByteCount); } break;
case PF_G16R16F: { FormatData<EDefaultInputType::Typed, TInType, FFloat16, 0, 1> (InData, OutData, OutByteCount); } break;
case PF_G16R16F_FILTER: { FormatData<EDefaultInputType::Typed, TInType, FFloat16, 0, 1> (InData, OutData, OutByteCount); } break;
case PF_R16F_FILTER: { FormatData<EDefaultInputType::Typed, TInType, FFloat16> (InData, OutData, OutByteCount); } break;
case PF_R16F: { FormatData<EDefaultInputType::Typed, TInType, FFloat16> (InData, OutData, OutByteCount); } break;
case PF_G16: { FormatData<EDefaultInputType::UNorm, TInType, uint16> (InData, OutData, OutByteCount); } break;
case PF_R16_UINT: { FormatData<EDefaultInputType::Typed, TInType, uint16> (InData, OutData, OutByteCount); } break;
case PF_R16_SINT: { FormatData<EDefaultInputType::Typed, TInType, int16> (InData, OutData, OutByteCount); } break;
// 8bits
case PF_B8G8R8A8: { FormatData<EDefaultInputType::UNorm, TInType, uint8, 2, 1, 0, 3> (InData, OutData, OutByteCount); } break;
case PF_R8G8B8A8: { FormatData<EDefaultInputType::UNorm, TInType, uint8, 0, 1, 2, 3> (InData, OutData, OutByteCount); } break;
case PF_A8R8G8B8: { FormatData<EDefaultInputType::UNorm, TInType, uint8, 3, 2, 1, 0> (InData, OutData, OutByteCount); } break;
case PF_R8G8B8A8_UINT: { FormatData<EDefaultInputType::Typed, TInType, uint8, 0, 1, 2, 3> (InData, OutData, OutByteCount); } break;
case PF_R8G8B8A8_SNORM: { FormatData<EDefaultInputType::SNorm, TInType, int8, 0, 1, 2, 3> (InData, OutData, OutByteCount); } break;
case PF_R8G8B8: { FormatData<EDefaultInputType::UNorm, TInType, uint8, 0, 1, 2> (InData, OutData, OutByteCount); } break;
case PF_R8G8: { FormatData<EDefaultInputType::UNorm, TInType, uint8, 0, 1> (InData, OutData, OutByteCount); } break;
case PF_R8G8_UINT: { FormatData<EDefaultInputType::Typed, TInType, uint8, 0, 1> (InData, OutData, OutByteCount); } break;
case PF_R8_UINT: { FormatData<EDefaultInputType::Typed, TInType, uint8> (InData, OutData, OutByteCount); } break;
case PF_R8_SINT: { FormatData<EDefaultInputType::Typed, TInType, int8> (InData, OutData, OutByteCount); } break;
case PF_R8: { FormatData<EDefaultInputType::UNorm, TInType, uint8> (InData, OutData, OutByteCount); } break;
case PF_G8: { FormatData<EDefaultInputType::UNorm, TInType, uint8> (InData, OutData, OutByteCount); } break;
case PF_L8: { FormatData<EDefaultInputType::UNorm, TInType, uint8> (InData, OutData, OutByteCount); } break;
case PF_A1: { FormatData<EDefaultInputType::UNorm, TInType, uint8> (InData, OutData, OutByteCount); } break;
case PF_A8: { FormatData<EDefaultInputType::UNorm, TInType, uint8> (InData, OutData, OutByteCount); } break;
// Depth/Stencil. Since these texture will only be used as SRV, we handle them as regular float/float16.
case PF_D24: { FormatData<EDefaultInputType::Typed, TInType, float> (InData, OutData, OutByteCount); } break;
case PF_DepthStencil: { FormatData<EDefaultInputType::Typed, TInType, float> (InData, OutData, OutByteCount); } break;
case PF_ShadowDepth: { FormatData<EDefaultInputType::Typed, TInType, FFloat16> (InData, OutData, OutByteCount); } break;
// Custom
case PF_FloatRGB: { FormatData111110<TInType> (InData, OutData, OutByteCount); } break;
case PF_A2B10G10R10: { FormatData1010102<TInType>(InData, OutData, OutByteCount); } break;
case PF_FloatR11G11B10: { FormatData111110<TInType> (InData, OutData, OutByteCount); } break;
case PF_B5G5R5A1_UNORM: { FormatData5551<TInType>(InData, OutData, OutByteCount); } break;
case PF_R9G9B9EXP5: { FormatData9995<TInType>(InData, OutData, OutByteCount); } break;
return;
// Not supported
case PF_R5G6B5_UNORM:
case PF_BC5:
case PF_V8U8:
case PF_PVRTC2:
case PF_PVRTC4:
case PF_UYVY:
case PF_DXT1:
case PF_DXT3:
case PF_DXT5:
case PF_BC4:
case PF_ATC_RGB:
case PF_ATC_RGBA_E:
case PF_ATC_RGBA_I:
case PF_X24_G8:
case PF_ETC1:
case PF_ETC2_RGB:
case PF_ETC2_RGBA:
case PF_ASTC_4x4:
case PF_ASTC_6x6:
case PF_ASTC_8x8:
case PF_ASTC_10x10:
case PF_ASTC_12x12:
case PF_ASTC_4x4_HDR:
case PF_ASTC_6x6_HDR:
case PF_ASTC_8x8_HDR:
case PF_ASTC_10x10_HDR:
case PF_ASTC_12x12_HDR:
case PF_ASTC_4x4_NORM_RG:
case PF_ASTC_6x6_NORM_RG:
case PF_ASTC_8x8_NORM_RG:
case PF_ASTC_10x10_NORM_RG:
case PF_ASTC_12x12_NORM_RG:
case PF_BC6H:
case PF_BC7:
case PF_XGXR8:
case PF_PLATFORM_HDR_0:
case PF_PLATFORM_HDR_1:
case PF_PLATFORM_HDR_2:
case PF_NV12:
case PF_ETC2_R11_EAC:
case PF_ETC2_RG11_EAC:
case PF_P010:
case PF_Unknown:
case PF_MAX:
OutByteCount = 0;
return;
}
}
template <typename DataType>
void SetDefaultTextureData2D(FRHITextureInitializer& Initializer, const FRHITextureCreateDesc& InDesc, const DataType& InData)
{
FRHITextureSubresourceInitializer Subresource = Initializer.GetTexture2DSubresource(0);
uint32 SrcByteCount = 0;
InitializeData(ToVector(InData), InDesc.Format, (uint8*)Subresource.Data, SrcByteCount);
}
template <typename DataType>
void SetDefaultTextureData2DArray(FRHITextureInitializer& Initializer, const FRHITextureCreateDesc& InDesc, const DataType& InData)
{
FRHITextureSubresourceInitializer Subresource = Initializer.GetTexture2DArraySubresource(0, 0);
uint32 SrcByteCount = 0;
InitializeData(ToVector(InData), InDesc.Format, (uint8*)Subresource.Data, SrcByteCount);
}
template <typename DataType>
void SetDefaultTextureData3D(FRHICommandListImmediate& RHICmdList, FRHITexture* Texture, const FRHITextureCreateDesc& InDesc, const DataType& InData)
{
uint8 SrcData[16] = { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, };
uint32 SrcByteCount = 0;
InitializeData(ToVector(InData), InDesc.Format, SrcData, SrcByteCount);
FUpdateTextureRegion3D Region(0, 0, 0, 0, 0, 0, 1, 1, 1);
RHICmdList.UpdateTexture3D(
Texture,
0,
Region,
SrcByteCount,
SrcByteCount,
SrcData);
// UpdateTexture3D before and after state is currently undefined
RHICmdList.Transition(FRHITransitionInfo(Texture, ERHIAccess::Unknown, ERHIAccess::SRVMask));
}
template <typename DataType>
void SetDefaultTextureDataCube(FRHITextureInitializer& Initializer, const FRHITextureCreateDesc& InDesc, const DataType& InData)
{
uint8 SrcData[16] = { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, };
uint32 SrcByteCount = 0;
InitializeData(ToVector(InData), InDesc.Format, SrcData, SrcByteCount);
for (uint32 FaceIt = 0; FaceIt < 6; ++FaceIt)
{
FRHITextureSubresourceInitializer Subresource = Initializer.GetTextureCubeSubresource(FaceIt, 0);
Subresource.WriteData(SrcData, SrcByteCount);
}
}
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);
// Convert Depth/Stencil format to float/float16 since these texture will only be used as SRV
if (Format == PF_D24 || Format == PF_DepthStencil) { Format = PF_R32_FLOAT; }
if (Format == PF_ShadowDepth) { Format = PF_R32_FLOAT; }
const FDefaultTextureKey Key = GetDefaultTextureKey(Dimension, 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, ERDGTextureFlags::SkipTracking);
}
FDefaultTexture Entry;
Entry.Key = Key;
Entry.Hash = Hash;
Entry.Texture = nullptr;
if (Dimension == ETextureDimension::Texture2D)
{
const FRHITextureCreateDesc Desc =
FRHITextureCreateDesc::Create2D(TEXT("DefaultTexture2D"), 1, 1, Format)
.SetFlags(ETextureCreateFlags::ShaderResource)
.SetInitActionInitializer();
FRHITextureInitializer Initializer = GraphBuilder.RHICmdList.CreateTextureInitializer(Desc);
SetDefaultTextureData2D(Initializer, Desc, Value);
FTextureRHIRef Texture = Initializer.Finalize();
Entry.Texture = CreateRenderTarget(Texture, Desc.DebugName);
}
else if (Dimension == ETextureDimension::Texture2DArray)
{
const FRHITextureCreateDesc Desc =
FRHITextureCreateDesc::Create2DArray(TEXT("DefaultTexture2DArray"), 1, 1, 1, Format)
.SetFlags(ETextureCreateFlags::ShaderResource)
.SetInitActionInitializer();
FRHITextureInitializer Initializer = GraphBuilder.RHICmdList.CreateTextureInitializer(Desc);
SetDefaultTextureData2DArray(Initializer, Desc, Value);
FTextureRHIRef Texture = Initializer.Finalize();
Entry.Texture = CreateRenderTarget(Texture, Desc.DebugName);
}
else if (Dimension == ETextureDimension::Texture3D)
{
const FRHITextureCreateDesc Desc =
FRHITextureCreateDesc::Create3D(TEXT("DefaultTexture3D"), 1, 1, 1, Format)
.SetFlags(ETextureCreateFlags::ShaderResource);
FTextureRHIRef Texture = RHICreateTexture(Desc);
SetDefaultTextureData3D(GraphBuilder.RHICmdList, Texture, Desc, Value);
Entry.Texture = CreateRenderTarget(Texture, Desc.DebugName);
}
else if (Dimension == ETextureDimension::TextureCube)
{
const FRHITextureCreateDesc Desc =
FRHITextureCreateDesc::CreateCube(TEXT("DefaultTextureCube"), 1, Format)
.SetFlags(ETextureCreateFlags::ShaderResource)
.SetInitActionInitializer();
FRHITextureInitializer Initializer = GraphBuilder.RHICmdList.CreateTextureInitializer(Desc);
SetDefaultTextureDataCube(Initializer, Desc, Value);
FTextureRHIRef Texture = Initializer.Finalize();
Entry.Texture = CreateRenderTarget(Texture, Desc.DebugName);
}
else if (Dimension == ETextureDimension::TextureCubeArray)
{
const FRHITextureCreateDesc Desc =
FRHITextureCreateDesc::CreateCubeArray(TEXT("DefaultTextureCubeArray"), 1, 1, Format)
.SetFlags(ETextureCreateFlags::ShaderResource)
.SetInitActionInitializer();
FRHITextureInitializer Initializer = GraphBuilder.RHICmdList.CreateTextureInitializer(Desc);
SetDefaultTextureDataCube(Initializer, Desc, Value);
FTextureRHIRef Texture = Initializer.Finalize();
Entry.Texture = CreateRenderTarget(Texture, Desc.DebugName);
}
else
{
return nullptr;
}
Index = DefaultTextures.Add(Entry);
HashDefaultTextures.Add(Hash, Index);
return GraphBuilder.RegisterExternalTexture(Entry.Texture, ERDGTextureFlags::SkipTracking);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// Default Buffers
template<typename T>
static FDefaultBufferKey GetDefaultBufferKey(uint32 NumBytePerElement, EDefaultBufferType BufferType, const T* In)
{
FDefaultBufferKey Out;
if (In)
{
const uint32 ClearValueNumBytes = sizeof(T);
const uint32* InAsUInt = (const uint32*)In;
Out.ValueAsUInt[0] = InAsUInt[0];
Out.ValueAsUInt[1] = ClearValueNumBytes > 4 ? InAsUInt[1] : 0u;
Out.ValueAsUInt[2] = ClearValueNumBytes > 8 ? InAsUInt[2] : 0u;
Out.ValueAsUInt[3] = ClearValueNumBytes > 12 ? InAsUInt[3] : 0u;
}
Out.NumBytePerElement = NumBytePerElement;
Out.BufferType = BufferType;
return Out;
}
bool operator !=(const FDefaultBufferKey& A, const FDefaultBufferKey& B)
{
return A.NumBytePerElement != B.NumBytePerElement ||
A.BufferType != B.BufferType ||
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,
EDefaultBufferType BufferType,
const TClearValue* Value)
{
// Buffer key
const uint32 NumElements = 1;
const FDefaultBufferKey Key = GetDefaultBufferKey(NumBytePerElement, BufferType, Value);
const uint32 Hash = Murmur32({(uint32)BufferType, Key.NumBytePerElement, Key.ValueAsUInt[0], Key.ValueAsUInt[1], Key.ValueAsUInt[2], Key.ValueAsUInt[3] });
// Find existing buffer ("fast" path)
uint32 Index = HashDefaultBuffers.First(Hash);
while (HashDefaultBuffers.IsValid(Index) && DefaultBuffers[Index].Key != Key)
{
Index = HashDefaultBuffers.Next(Index);
}
if (HashDefaultBuffers.IsValid(Index) && DefaultBuffers[Index].Buffer != nullptr)
{
check(DefaultBuffers[Index].Hash == Hash); //Sanitycheck
return GraphBuilder.RegisterExternalBuffer(DefaultBuffers[Index].Buffer, ERDGBufferFlags::SkipTracking);
}
const uint32 BufferSize = NumBytePerElement * NumElements;
const TCHAR* BufferName = TEXT("");
FRDGBufferDesc BufferDesc;
FRHICommandListBase& RHICmdList = GraphBuilder.RHICmdList;
// Adding new buffer if there is no fit (slow path)
TRefCountPtr<FRHIBuffer> RHIBuffer;
switch (BufferType)
{
case EDefaultBufferType::VertexBuffer:
{
BufferName = TEXT("DefaultBuffer");
BufferDesc = FRDGBufferDesc::CreateUploadDesc(NumBytePerElement, NumElements);
const FRHIBufferCreateDesc BufferCreateDesc =
FRHIBufferCreateDesc::CreateVertex(BufferName, BufferSize)
.AddUsage(EBufferUsageFlags::Static | EBufferUsageFlags::ShaderResource)
.DetermineInitialState();
RHIBuffer = RHICmdList.CreateBuffer(BufferCreateDesc);
}
break;
case EDefaultBufferType::StructuredBuffer:
{
BufferName = TEXT("DefaultStructuredBuffer");
BufferDesc = FRDGBufferDesc::CreateStructuredDesc(NumBytePerElement, NumElements);
// Remove the UAV flag, as default resources are supposed to be read-only.
EnumRemoveFlags(BufferDesc.Usage, EBufferUsageFlags::UnorderedAccess);
const FRHIBufferCreateDesc BufferCreateDesc =
FRHIBufferCreateDesc::CreateStructured(BufferName, BufferSize, NumBytePerElement)
.AddUsage(BufferDesc.Usage)
.DetermineInitialState();
RHIBuffer = RHICmdList.CreateBuffer(BufferCreateDesc);
}
break;
case EDefaultBufferType::ByteAddressBuffer:
{
BufferName = TEXT("DefaultByteAddressBuffer");
BufferDesc = FRDGBufferDesc::CreateByteAddressDesc(BufferSize);
// Same as above.
EnumRemoveFlags(BufferDesc.Usage, EBufferUsageFlags::UnorderedAccess);
const FRHIBufferCreateDesc BufferCreateDesc =
FRHIBufferCreateDesc::CreateStructured(BufferName, BufferSize, NumBytePerElement)
.AddUsage(BufferDesc.Usage)
.DetermineInitialState();
RHIBuffer = RHICmdList.CreateBuffer(BufferCreateDesc);
}
break;
}
uint8* DestPtr = static_cast<uint8*>(RHICmdList.LockBuffer(RHIBuffer, 0, BufferSize, RLM_WriteOnly));
if (Value)
{
const uint8 *EndPtr = DestPtr + BufferSize;
for (uint32 Offset = 0; Offset < (BufferSize / sizeof(TClearValue)); Offset++)
{
FMemory::Memcpy(DestPtr, Value, sizeof(TClearValue));
DestPtr += sizeof(TClearValue);
}
if (DestPtr < EndPtr)
{
// Zero out the remainder. Byte-splitting the init value is undefined.
FMemory::Memzero(DestPtr, static_cast<SIZE_T>(EndPtr - DestPtr));
}
}
else
{
FMemory::Memzero(DestPtr, BufferSize);
}
RHICmdList.UnlockBuffer(RHIBuffer);
FDefaultBuffer Entry;
Entry.Key = Key;
Entry.Hash = Hash;
Entry.Buffer = new FRDGPooledBuffer(RHICmdList, RHIBuffer, BufferDesc, NumElements, BufferName);
Index = DefaultBuffers.Add(Entry);
HashDefaultBuffers.Add(Hash, Index);
return GraphBuilder.RegisterExternalBuffer(Entry.Buffer, ERDGBufferFlags::SkipTracking);
}
FVector4f GetClearBindingValue(EPixelFormat Format, FClearValueBinding Value)
{
if (IsDepthOrStencilFormat(Format))
{
return FVector4f(Value.Value.DSValue.Depth, Value.Value.DSValue.Depth, Value.Value.DSValue.Depth, Value.Value.DSValue.Depth);
}
else
{
return FVector4f(Value.Value.Color[0], Value.Value.Color[1], Value.Value.Color[2], Value.Value.Color[3]);
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 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 FVector3f& Value) { return GetInternalDefaultTexture(GraphBuilder, DefaultTextures, HashDefaultTextures, ETextureDimension::Texture2D, Format, Value); }
FRDGTextureRef FSystemTextures::GetDefaultTexture2D(FRDGBuilder& GraphBuilder, EPixelFormat Format, const FVector4f& 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, GetClearBindingValue(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 FVector3f& Value) { return GetInternalDefaultTexture(GraphBuilder, DefaultTextures, HashDefaultTextures, Dimension, Format, Value); }
FRDGTextureRef FSystemTextures::GetDefaultTexture(FRDGBuilder& GraphBuilder, ETextureDimension Dimension, EPixelFormat Format, const FVector4f& 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, GetClearBindingValue(Format, Value)); }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Buffers
// Default init to 0
FRDGBufferRef FSystemTextures::GetDefaultBuffer(FRDGBuilder& GraphBuilder, uint32 NumBytePerElement) { return GetInternalDefaultBuffer(GraphBuilder, DefaultBuffers, HashDefaultBuffers, NumBytePerElement, EDefaultBufferType::VertexBuffer, (uint32*)nullptr); }
FRDGBufferRef FSystemTextures::GetDefaultStructuredBuffer(FRDGBuilder& GraphBuilder, uint32 NumBytePerElement) { return GetInternalDefaultBuffer(GraphBuilder, DefaultBuffers, HashDefaultBuffers, NumBytePerElement, EDefaultBufferType::StructuredBuffer, (uint32*)nullptr); }
FRDGBufferRef FSystemTextures::GetDefaultByteAddressBuffer(FRDGBuilder& GraphBuilder, uint32 NumBytePerElement) { return GetInternalDefaultBuffer(GraphBuilder, DefaultBuffers, HashDefaultBuffers, NumBytePerElement, EDefaultBufferType::ByteAddressBuffer, (uint32*)nullptr); }
// Default value of an element
FRDGBufferRef FSystemTextures::GetDefaultBuffer(FRDGBuilder& GraphBuilder, uint32 NumBytePerElement, float Value) { return GetInternalDefaultBuffer(GraphBuilder, DefaultBuffers, HashDefaultBuffers, NumBytePerElement, EDefaultBufferType::VertexBuffer, &Value); }
FRDGBufferRef FSystemTextures::GetDefaultBuffer(FRDGBuilder& GraphBuilder, uint32 NumBytePerElement, uint32 Value) { return GetInternalDefaultBuffer(GraphBuilder, DefaultBuffers, HashDefaultBuffers, NumBytePerElement, EDefaultBufferType::VertexBuffer, &Value); }
FRDGBufferRef FSystemTextures::GetDefaultBuffer(FRDGBuilder& GraphBuilder, uint32 NumBytePerElement, const FVector3f& Value) { return GetInternalDefaultBuffer(GraphBuilder, DefaultBuffers, HashDefaultBuffers, NumBytePerElement, EDefaultBufferType::VertexBuffer, &Value); }
FRDGBufferRef FSystemTextures::GetDefaultBuffer(FRDGBuilder& GraphBuilder, uint32 NumBytePerElement, const FVector4f& Value) { return GetInternalDefaultBuffer(GraphBuilder, DefaultBuffers, HashDefaultBuffers, NumBytePerElement, EDefaultBufferType::VertexBuffer, &Value); }
FRDGBufferRef FSystemTextures::GetDefaultBuffer(FRDGBuilder& GraphBuilder, uint32 NumBytePerElement, const FUintVector4& Value) { return GetInternalDefaultBuffer(GraphBuilder, DefaultBuffers, HashDefaultBuffers, NumBytePerElement, EDefaultBufferType::VertexBuffer, &Value); }
FRDGBufferRef FSystemTextures::GetDefaultStructuredBuffer(FRDGBuilder& GraphBuilder, uint32 NumBytePerElement, float Value) { return GetInternalDefaultBuffer(GraphBuilder, DefaultBuffers, HashDefaultBuffers, NumBytePerElement, EDefaultBufferType::StructuredBuffer, &Value); }
FRDGBufferRef FSystemTextures::GetDefaultStructuredBuffer(FRDGBuilder& GraphBuilder, uint32 NumBytePerElement, uint32 Value) { return GetInternalDefaultBuffer(GraphBuilder, DefaultBuffers, HashDefaultBuffers, NumBytePerElement, EDefaultBufferType::StructuredBuffer, &Value); }
FRDGBufferRef FSystemTextures::GetDefaultStructuredBuffer(FRDGBuilder& GraphBuilder, uint32 NumBytePerElement, const FVector3f& Value) { return GetInternalDefaultBuffer(GraphBuilder, DefaultBuffers, HashDefaultBuffers, NumBytePerElement, EDefaultBufferType::StructuredBuffer, &Value); }
FRDGBufferRef FSystemTextures::GetDefaultStructuredBuffer(FRDGBuilder& GraphBuilder, uint32 NumBytePerElement, const FVector4f& Value) { return GetInternalDefaultBuffer(GraphBuilder, DefaultBuffers, HashDefaultBuffers, NumBytePerElement, EDefaultBufferType::StructuredBuffer, &Value); }
FRDGBufferRef FSystemTextures::GetDefaultStructuredBuffer(FRDGBuilder& GraphBuilder, uint32 NumBytePerElement, const FUintVector4& Value) { return GetInternalDefaultBuffer(GraphBuilder, DefaultBuffers, HashDefaultBuffers, NumBytePerElement, EDefaultBufferType::StructuredBuffer, &Value); }