// Copyright Epic Games, Inc. All Rights Reserved. #include "VT/RuntimeVirtualTextureRender.h" #include "ComponentRecreateRenderStateContext.h" #include "Components/RuntimeVirtualTextureComponent.h" #include "DataDrivenShaderPlatformInfo.h" #include "EngineModule.h" #include "GlobalShader.h" #include "GPUScene.h" #include "MaterialShader.h" #include "MeshPassProcessor.h" #include "MeshPassProcessor.inl" #include "PostProcess/SceneRenderTargets.h" #include "ProfilingDebugging/CsvProfiler.h" #include "RenderCaptureInterface.h" #include "RenderGraphBuilder.h" #include "RenderGraphUtils.h" #include "RenderUtils.h" #include "RHIResourceUtils.h" #include "ScenePrivate.h" #include "SceneRendering.h" #include "ShaderPlatformCachedIniValue.h" #include "ShaderBaseClasses.h" #include "SimpleMeshDrawCommandPass.h" #include "StaticMeshBatch.h" #include "VT/RuntimeVirtualTexture.h" #include "VT/RuntimeVirtualTextureSceneExtension.h" #include "VT/RuntimeVirtualTextureSceneProxy.h" CSV_DECLARE_CATEGORY_EXTERN(VirtualTexturing); DECLARE_DWORD_COUNTER_STAT(TEXT("Num pages rendered"), STAT_RenderedPages, STATGROUP_VirtualTexturing); namespace RuntimeVirtualTexture { static TAutoConsoleVariable CVarVTMipColors( TEXT("r.VT.RVT.MipColors"), 0, TEXT("Render mip colors to RVT BaseColor.\n 0 off. 1 shows all mip colors. 2 shows only mip 0."), FConsoleVariableDelegate::CreateLambda([](IConsoleVariable* InVariable) { FGlobalComponentRecreateRenderStateContext Context; }), ECVF_RenderThreadSafe); static TAutoConsoleVariable CVarVTHighQualityPerPixelHeight( TEXT("r.VT.RVT.HighQualityPerPixelHeight"), 1, TEXT("Use higher quality sampling of per pixel heightmaps when rendering to Runtime Virtual Texture.\n"), ECVF_ReadOnly); static TAutoConsoleVariable CVarVTDirectCompress( TEXT("r.VT.RVT.DirectCompress"), 1, TEXT("Compress texture data direct to the physical texture on platforms that support it."), ECVF_RenderThreadSafe); static TAutoConsoleVariable CVarVTApplyPageCorruptionFix( TEXT("r.VT.RVT.PageCorruptionFix"), false, TEXT("Apply change that has been found to fix some rare page corruption on PC."), ECVF_RenderThreadSafe); int32 RenderCaptureNextRVTPagesDraws = 0; static FAutoConsoleVariableRef CVarRenderCaptureNextRVTPagesDraws( TEXT("r.VT.RenderCaptureNextPagesDraws"), RenderCaptureNextRVTPagesDraws, TEXT("Trigger a render capture during the next RVT RenderPages draw calls.")); static TAutoConsoleVariable CVarRVTAstc( TEXT("r.VT.RVT.ASTC"), 0, TEXT("Use ASTC compression instead of ETC2 when the hardware supports it."), ECVF_ReadOnly); static TAutoConsoleVariable CVarRVTAstcHigh( TEXT("r.VT.RVT.ASTC.High"), 0, TEXT("When using ASTC compression, produce higher quality output at roughly 2x the time spent encoding."), ECVF_Default); BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FEtcParameters, ) SHADER_PARAMETER_ARRAY(FVector4f, ALPHA_DISTANCE_TABLES, [16]) SHADER_PARAMETER_ARRAY(FVector4f, RGB_DISTANCE_TABLES, [8]) END_GLOBAL_SHADER_PARAMETER_STRUCT() IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT(FEtcParameters, "EtcParameters"); static TGlobalResource RuntimeVirtualReadBuffer; class FEtcParametersUniformBuffer : public TUniformBuffer { typedef TUniformBuffer Super; public: FEtcParametersUniformBuffer() { FEtcParameters Parameters; Parameters.ALPHA_DISTANCE_TABLES[0] = FVector4f(2, 5, 8, 14); Parameters.ALPHA_DISTANCE_TABLES[1] = FVector4f(2, 6, 9, 12); Parameters.ALPHA_DISTANCE_TABLES[2] = FVector4f(1, 4, 7, 12); Parameters.ALPHA_DISTANCE_TABLES[3] = FVector4f(1, 3, 5, 12); Parameters.ALPHA_DISTANCE_TABLES[4] = FVector4f(2, 5, 7, 11); Parameters.ALPHA_DISTANCE_TABLES[5] = FVector4f(2, 6, 8, 10); Parameters.ALPHA_DISTANCE_TABLES[6] = FVector4f(3, 6, 7, 10); Parameters.ALPHA_DISTANCE_TABLES[7] = FVector4f(2, 4, 7, 10); Parameters.ALPHA_DISTANCE_TABLES[8] = FVector4f(1, 5, 7, 9); Parameters.ALPHA_DISTANCE_TABLES[9] = FVector4f(1, 4, 7, 9); Parameters.ALPHA_DISTANCE_TABLES[10] = FVector4f(1, 3, 7, 9); Parameters.ALPHA_DISTANCE_TABLES[11] = FVector4f(1, 4, 6, 9); Parameters.ALPHA_DISTANCE_TABLES[12] = FVector4f(2, 3, 6, 9); Parameters.ALPHA_DISTANCE_TABLES[13] = FVector4f(0, 1, 2, 9); Parameters.ALPHA_DISTANCE_TABLES[14] = FVector4f(3, 5, 7, 8); Parameters.ALPHA_DISTANCE_TABLES[15] = FVector4f(2, 4, 6, 8); Parameters.RGB_DISTANCE_TABLES[0] = FVector4f(-8, -2, 2, 8); Parameters.RGB_DISTANCE_TABLES[1] = FVector4f(-17, -5, 5, 17); Parameters.RGB_DISTANCE_TABLES[2] = FVector4f(-29, -9, 9, 29); Parameters.RGB_DISTANCE_TABLES[3] = FVector4f(-42, -13, 13, 42); Parameters.RGB_DISTANCE_TABLES[4] = FVector4f(-60, -18, 18, 60); Parameters.RGB_DISTANCE_TABLES[5] = FVector4f(-80, -24, 24, 80); Parameters.RGB_DISTANCE_TABLES[6] = FVector4f(-106, -33, 33, 106); Parameters.RGB_DISTANCE_TABLES[7] = FVector4f(-183, -47, 47, 183); SetContentsNoUpdate(Parameters); } }; const TUniformBufferRef& GetEtcParametersUniformBufferRef() { static TGlobalResource EtcParametersUniformBuffer; return EtcParametersUniformBuffer.GetUniformBufferRef(); } static const uint8 TritsToInteger[243] = { 0, 1, 2, 4, 5, 6, 8, 9, 10, 16, 17, 18, 20, 21, 22, 24, 25, 26, 3, 7, 15, 19, 23, 27, 12, 13, 14, 32, 33, 34, 36, 37, 38, 40, 41, 42, 48, 49, 50, 52, 53, 54, 56, 57, 58, 35, 39, 47, 51, 55, 59, 44, 45, 46, 64, 65, 66, 68, 69, 70, 72, 73, 74, 80, 81, 82, 84, 85, 86, 88, 89, 90, 67, 71, 79, 83, 87, 91, 76, 77, 78, 128, 129, 130, 132, 133, 134, 136, 137, 138, 144, 145, 146, 148, 149, 150, 152, 153, 154, 131, 135, 143, 147, 151, 155, 140, 141, 142, 160, 161, 162, 164, 165, 166, 168, 169, 170, 176, 177, 178, 180, 181, 182, 184, 185, 186, 163, 167, 175, 179, 183, 187, 172, 173, 174, 192, 193, 194, 196, 197, 198, 200, 201, 202, 208, 209, 210, 212, 213, 214, 216, 217, 218, 195, 199, 207, 211, 215, 219, 204, 205, 206, 96, 97, 98, 100, 101, 102, 104, 105, 106, 112, 113, 114, 116, 117, 118, 120, 121, 122, 99, 103, 111, 115, 119, 123, 108, 109, 110, 224, 225, 226, 228, 229, 230, 232, 233, 234, 240, 241, 242, 244, 245, 246, 248, 249, 250, 227, 231, 239, 243, 247, 251, 236, 237, 238, 28, 29, 30, 60, 61, 62, 92, 93, 94, 156, 157, 158, 188, 189, 190, 220, 221, 222, 31, 63, 127, 159, 191, 255, 252, 253, 254 }; static const uint8 QuintsToInteger[125] = { 0, 1, 2, 3, 4, 8, 9, 10, 11, 12, 16, 17, 18, 19, 20, 24, 25, 26, 27, 28, 5, 13, 21, 29, 6, 32, 33, 34, 35, 36, 40, 41, 42, 43, 44, 48, 49, 50, 51, 52, 56, 57, 58, 59, 60, 37, 45, 53, 61, 14, 64, 65, 66, 67, 68, 72, 73, 74, 75, 76, 80, 81, 82, 83, 84, 88, 89, 90, 91, 92, 69, 77, 85, 93, 22, 96, 97, 98, 99, 100, 104, 105, 106, 107, 108, 112, 113, 114, 115, 116, 120, 121, 122, 123, 124, 101, 109, 117, 125, 30, 102, 103, 70, 71, 38, 110, 111, 78, 79, 46, 118, 119, 86, 87, 54, 126, 127, 94, 95, 62, 39, 47, 55, 63, 31 }; // from [ARM:astc-encoder] quantization_and_transfer_table quant_and_xfer_tables #define WEIGHT_QUANTIZE_NUM 32 static const uint8 ScrambleTable[12 * WEIGHT_QUANTIZE_NUM] = { // quantization method 0, range 0..1 //{ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //}, // quantization method 1, range 0..2 //{ 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //}, // quantization method 2, range 0..3 //{ 0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //}, // quantization method 3, range 0..4 //{ 0, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //}, // quantization method 4, range 0..5 //{ 0, 2, 4, 5, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //}, // quantization method 5, range 0..7 //{ 0, 1, 2, 3, 4, 5, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //}, // quantization method 6, range 0..9 //{ 0, 2, 4, 6, 8, 9, 7, 5, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //}, // quantization method 7, range 0..11 //{ 0, 4, 8, 2, 6, 10, 11, 7, 3, 9, 5, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //}, // quantization method 8, range 0..15 //{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //}, // quantization method 9, range 0..19 //{ 0, 4, 8, 12, 16, 2, 6, 10, 14, 18, 19, 15, 11, 7, 3, 17, 13, 9, 5, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //}, // quantization method 10, range 0..23 //{ 0, 8, 16, 2, 10, 18, 4, 12, 20, 6, 14, 22, 23, 15, 7, 21, 13, 5, 19, 11, 3, 17, 9, 1, 0, 0, 0, 0, 0, 0, 0, 0, //}, // quantization method 11, range 0..31 //{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, //} }; static const uint8 ColorScrambleTable48[256] = { 0, 0, 0, 16, 16, 16, 16, 16, 32, 32, 32, 32, 32, 32, 2, 2, 2, 2, 2, 18, 18, 18, 18, 18, 34, 34, 34, 34, 34, 34, 4, 4, 4, 4, 4, 20, 20, 20, 20, 20, 20, 36, 36, 36, 36, 36, 6, 6, 6, 6, 6, 22, 22, 22, 22, 22, 22, 38, 38, 38, 38, 38, 38, 8, 8, 8, 8, 8, 24, 24, 24, 24, 24, 40, 40, 40, 40, 40, 40, 10, 10, 10, 10, 10, 26, 26, 26, 26, 26, 42, 42, 42, 42, 42, 42, 12, 12, 12, 12, 12, 28, 28, 28, 28, 28, 28, 44, 44, 44, 44, 44, 14, 14, 14, 14, 14, 30, 30, 30, 30, 30, 30, 46, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 31, 31, 31, 31, 31, 31, 15, 15, 15, 15, 15, 45, 45, 45, 45, 45, 29, 29, 29, 29, 29, 29, 13, 13, 13, 13, 13, 43, 43, 43, 43, 43, 43, 27, 27, 27, 27, 27, 11, 11, 11, 11, 11, 41, 41, 41, 41, 41, 41, 25, 25, 25, 25, 25, 9, 9, 9, 9, 9, 39, 39, 39, 39, 39, 39, 23, 23, 23, 23, 23, 23, 7, 7, 7, 7, 7, 37, 37, 37, 37, 37, 21, 21, 21, 21, 21, 21, 5, 5, 5, 5, 5, 35, 35, 35, 35, 35, 35, 19, 19, 19, 19, 19, 3, 3, 3, 3, 3, 33, 33, 33, 33, 33, 33, 17, 17, 17, 17, 17, 1, 1, 1 }; static const uint8 ColorScrambleTable80[256] = { 0, 0, 16, 16, 16, 32, 32, 32, 48, 48, 48, 64, 64, 64, 64, 2, 2, 2, 18, 18, 18, 34, 34, 34, 50, 50, 50, 66, 66, 66, 66, 4, 4, 4, 20, 20, 20, 36, 36, 36, 52, 52, 52, 52, 68, 68, 68, 6, 6, 6, 22, 22, 22, 38, 38, 38, 54, 54, 54, 54, 70, 70, 70, 8, 8, 8, 24, 24, 24, 40, 40, 40, 40, 56, 56, 56, 72, 72, 72, 10, 10, 10, 26, 26, 26, 42, 42, 42, 42, 58, 58, 58, 74, 74, 74, 12, 12, 12, 28, 28, 28, 28, 44, 44, 44, 60, 60, 60, 76, 76, 76, 14, 14, 14, 30, 30, 30, 30, 46, 46, 46, 62, 62, 62, 78, 78, 78, 78, 79, 79, 79, 79, 63, 63, 63, 47, 47, 47, 31, 31, 31, 31, 15, 15, 15, 77, 77, 77, 61, 61, 61, 45, 45, 45, 29, 29, 29, 29, 13, 13, 13, 75, 75, 75, 59, 59, 59, 43, 43, 43, 43, 27, 27, 27, 11, 11, 11, 73, 73, 73, 57, 57, 57, 41, 41, 41, 41, 25, 25, 25, 9, 9, 9, 71, 71, 71, 55, 55, 55, 55, 39, 39, 39, 23, 23, 23, 7, 7, 7, 69, 69, 69, 53, 53, 53, 53, 37, 37, 37, 21, 21, 21, 5, 5, 5, 67, 67, 67, 67, 51, 51, 51, 35, 35, 35, 19, 19, 19, 3, 3, 3, 65, 65, 65, 65, 49, 49, 49, 33, 33, 33, 17, 17, 17, 1, 1 }; static const uint8 ColorScrambleTable192[256] = { 0, 64, 128, 128, 2, 66, 130, 130, 4, 68, 132, 132, 6, 70, 134, 134, 8, 72, 136, 136, 10, 74, 138, 138, 12, 76, 140, 140, 14, 78, 142, 142, 16, 80, 144, 144, 18, 82, 146, 146, 20, 84, 148, 148, 22, 86, 150, 150, 24, 88, 152, 152, 26, 90, 154, 154, 28, 92, 156, 156, 30, 94, 158, 158, 32, 96, 160, 160, 34, 98, 162, 162, 36, 100, 164, 164, 38, 102, 166, 166, 40, 104, 168, 168, 42, 106, 170, 170, 44, 108, 172, 172, 46, 110, 174, 174, 48, 112, 176, 176, 50, 114, 178, 178, 52, 116, 180, 180, 54, 118, 182, 182, 56, 120, 184, 184, 58, 122, 186, 186, 60, 124, 188, 188, 62, 126, 190, 190, 191, 191, 127, 63, 189, 189, 125, 61, 187, 187, 123, 59, 185, 185, 121, 57, 183, 183, 119, 55, 181, 181, 117, 53, 179, 179, 115, 51, 177, 177, 113, 49, 175, 175, 111, 47, 173, 173, 109, 45, 171, 171, 107, 43, 169, 169, 105, 41, 167, 167, 103, 39, 165, 165, 101, 37, 163, 163, 99, 35, 161, 161, 97, 33, 159, 159, 95, 31, 157, 157, 93, 29, 155, 155, 91, 27, 153, 153, 89, 25, 151, 151, 87, 23, 149, 149, 85, 21, 147, 147, 83, 19, 145, 145, 81, 17, 143, 143, 79, 15, 141, 141, 77, 13, 139, 139, 75, 11, 137, 137, 73, 9, 135, 135, 71, 7, 133, 133, 69, 5, 131, 131, 67, 3, 129, 129, 65, 1 }; BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FAstcParameters, ) SHADER_PARAMETER_SRV(Buffer, TritsToInteger) SHADER_PARAMETER_SRV(Buffer, QuintsToInteger) SHADER_PARAMETER_SRV(Buffer, ScrambleTable) SHADER_PARAMETER_SRV(Buffer, ColorScrambleTable48) SHADER_PARAMETER_SRV(Buffer, ColorScrambleTable80) SHADER_PARAMETER_SRV(Buffer, ColorScrambleTable192) END_GLOBAL_SHADER_PARAMETER_STRUCT() IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT(FAstcParameters, "AstcParameters"); class FAstcParametersUniformBuffer : public TUniformBuffer { typedef TUniformBuffer Super; public: virtual void InitRHI(FRHICommandListBase& RHICmdList) override { FAstcParameters Parameters; { const FRHIBufferCreateDesc CreateDesc = FRHIBufferCreateDesc::CreateVertex(TEXT("TritsToInteger"), 243) .AddUsage(EBufferUsageFlags::Static | EBufferUsageFlags::ShaderResource) .SetInitialState(ERHIAccess::VertexOrIndexBuffer | ERHIAccess::SRVMask); Parameters.TritsToInteger = RHICmdList.CreateShaderResourceView( UE::RHIResourceUtils::CreateBufferWithArray(RHICmdList, CreateDesc, TritsToInteger), FRHIViewDesc::CreateBufferSRV() .SetType(FRHIViewDesc::EBufferType::Typed) .SetFormat(PF_R8_UINT)); } { const FRHIBufferCreateDesc CreateDesc = FRHIBufferCreateDesc::CreateVertex(TEXT("QuintsToInteger"), 125) .AddUsage(EBufferUsageFlags::Static | EBufferUsageFlags::ShaderResource) .SetInitialState(ERHIAccess::VertexOrIndexBuffer | ERHIAccess::SRVMask); Parameters.QuintsToInteger = RHICmdList.CreateShaderResourceView( UE::RHIResourceUtils::CreateBufferWithArray(RHICmdList, CreateDesc, QuintsToInteger), FRHIViewDesc::CreateBufferSRV() .SetType(FRHIViewDesc::EBufferType::Typed) .SetFormat(PF_R8_UINT)); } { const FRHIBufferCreateDesc CreateDesc = FRHIBufferCreateDesc::CreateVertex(TEXT("ScrambleTable"), 12 * WEIGHT_QUANTIZE_NUM) .AddUsage(EBufferUsageFlags::Static | EBufferUsageFlags::ShaderResource) .SetInitialState(ERHIAccess::VertexOrIndexBuffer | ERHIAccess::SRVMask); Parameters.ScrambleTable = RHICmdList.CreateShaderResourceView( UE::RHIResourceUtils::CreateBufferWithArray(RHICmdList, CreateDesc, ScrambleTable), FRHIViewDesc::CreateBufferSRV() .SetType(FRHIViewDesc::EBufferType::Typed) .SetFormat(PF_R8_UINT)); } { const FRHIBufferCreateDesc CreateDesc = FRHIBufferCreateDesc::CreateVertex(TEXT("ColorScrambleTable48"), 256) .AddUsage(EBufferUsageFlags::Static | EBufferUsageFlags::ShaderResource) .SetInitialState(ERHIAccess::VertexOrIndexBuffer | ERHIAccess::SRVMask); Parameters.ColorScrambleTable48 = RHICmdList.CreateShaderResourceView( UE::RHIResourceUtils::CreateBufferWithArray(RHICmdList, CreateDesc, ColorScrambleTable48), FRHIViewDesc::CreateBufferSRV() .SetType(FRHIViewDesc::EBufferType::Typed) .SetFormat(PF_R8_UINT)); } { const FRHIBufferCreateDesc CreateDesc = FRHIBufferCreateDesc::CreateVertex(TEXT("ColorScrambleTable80"), 256) .AddUsage(EBufferUsageFlags::Static | EBufferUsageFlags::ShaderResource) .SetInitialState(ERHIAccess::VertexOrIndexBuffer | ERHIAccess::SRVMask); Parameters.ColorScrambleTable80 = RHICmdList.CreateShaderResourceView( UE::RHIResourceUtils::CreateBufferWithArray(RHICmdList, CreateDesc, ColorScrambleTable80), FRHIViewDesc::CreateBufferSRV() .SetType(FRHIViewDesc::EBufferType::Typed) .SetFormat(PF_R8_UINT)); } { const FRHIBufferCreateDesc CreateDesc = FRHIBufferCreateDesc::CreateVertex(TEXT("ColorScrambleTable192"), 256) .AddUsage(EBufferUsageFlags::Static | EBufferUsageFlags::ShaderResource) .SetInitialState(ERHIAccess::VertexOrIndexBuffer | ERHIAccess::SRVMask); Parameters.ColorScrambleTable192 = RHICmdList.CreateShaderResourceView( UE::RHIResourceUtils::CreateBufferWithArray(RHICmdList, CreateDesc, ColorScrambleTable192), FRHIViewDesc::CreateBufferSRV() .SetType(FRHIViewDesc::EBufferType::Typed) .SetFormat(PF_R8_UINT)); } SetContentsNoUpdate(Parameters); Super::InitRHI(RHICmdList); } }; const TUniformBufferRef& GetAstcParametersUniformBufferRef() { static TGlobalResource AstcParametersUniformBuffer; return AstcParametersUniformBuffer.GetUniformBufferRef(); } bool UseEtcProfile(EShaderPlatform ShaderPlatform) { switch (ShaderPlatform) { case SP_METAL_ES3_1_IOS: case SP_METAL_SM5_IOS: case SP_METAL_SIM: case SP_METAL_ES3_1_TVOS: case SP_METAL_SM5_TVOS: case SP_VULKAN_ES3_1_ANDROID: case SP_OPENGL_ES3_1_ANDROID: case SP_VULKAN_SM5_ANDROID: return true; default: break; } return false; } bool UseAstcProfile(EShaderPlatform ShaderPlatform) { if (!CVarRVTAstc.GetValueOnAnyThread()) { return false; } switch (ShaderPlatform) { case SP_METAL_ES3_1_IOS: case SP_METAL_SM5_IOS: case SP_METAL_SIM: case SP_METAL_ES3_1_TVOS: case SP_METAL_SM5_TVOS: case SP_VULKAN_ES3_1_ANDROID: case SP_OPENGL_ES3_1_ANDROID: case SP_VULKAN_SM5_ANDROID: return true; default: break; } return false; } bool UseAstcHighProfile(EShaderPlatform ShaderPlatform) { return UseAstcProfile(ShaderPlatform) && CVarRVTAstcHigh.GetValueOnAnyThread(); } /** For platforms that do not support 2-channel images, write 64bit compressed texture outputs into RGBA16 instead of RG32. */ bool UseRGBA16(EShaderPlatform ShaderPlatform) { return IsOpenGLPlatform(ShaderPlatform); } /** Parameters used when writing to the virtual texture. */ BEGIN_UNIFORM_BUFFER_STRUCT(FRuntimeVirtualTexturePassParameters, ) SHADER_PARAMETER(FVector4f, MipLevel) SHADER_PARAMETER(FVector4f, CustomMaterialData) SHADER_PARAMETER(FVector4f, DebugParams) SHADER_PARAMETER(FVector2f, PackHeight) END_UNIFORM_BUFFER_STRUCT() /** Uniform buffer for writing to the virtual texture. We reuse the DeferredDecals UB slot, which can't be used at the same time. This avoids the overhead of a new slot. */ IMPLEMENT_STATIC_UNIFORM_BUFFER_STRUCT(FRuntimeVirtualTexturePassParameters, "RuntimeVirtualTexturePassParameters", DeferredDecals); /** Mesh material shader for writing to the virtual texture. */ class FShader_VirtualTextureMaterialDraw : public FMeshMaterialShader { public: BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneUniformParameters, Scene) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FRuntimeVirtualTexturePassParameters, RuntimeVirtualTexturePassParameters) SHADER_PARAMETER_STRUCT_INCLUDE(FInstanceCullingDrawParams, InstanceCullingDrawParams) RENDER_TARGET_BINDING_SLOTS() END_SHADER_PARAMETER_STRUCT() static bool ShouldCompilePermutation(const FMeshMaterialShaderPermutationParameters& Parameters) { return UseVirtualTexturing(Parameters.Platform) && (Parameters.MaterialParameters.bHasRuntimeVirtualTextureOutput || Parameters.MaterialParameters.bIsDefaultMaterial); } static void ModifyCompilationEnvironment(const FMaterialShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FMeshMaterialShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("IS_VIRTUAL_TEXTURE_MATERIAL"), 1); static FShaderPlatformCachedIniValue HighQualityPerPixelHeightValue(TEXT("r.VT.RVT.HighQualityPerPixelHeight")); const bool bHighQualityPerPixelHeight = (HighQualityPerPixelHeightValue.Get((EShaderPlatform)Parameters.Platform) != 0); OutEnvironment.SetDefine(TEXT("PER_PIXEL_HEIGHTMAP_HQ"), bHighQualityPerPixelHeight ? 1 : 0); } FShader_VirtualTextureMaterialDraw() {} FShader_VirtualTextureMaterialDraw(const FMeshMaterialShaderType::CompiledShaderInitializerType& Initializer) : FMeshMaterialShader(Initializer) { } }; /** Specialization for ERuntimeVirtualTextureMaterialType::BaseColor */ class FMaterialPolicy_BaseColor { public: static bool ShouldCompilePermutation(const FMeshMaterialShaderPermutationParameters& Parameters) { return RuntimeVirtualTexture::IsMaterialTypeSupported(ERuntimeVirtualTextureMaterialType::BaseColor, Parameters.Platform); } static void ModifyCompilationEnvironment(FShaderCompilerEnvironment& OutEnvironment) { OutEnvironment.SetDefine(TEXT("OUT_BASECOLOR"), 1); } static FRHIBlendState* GetBlendState(uint8 OutputAttributeMask) { return TStaticBlendState< CW_RGBA, BO_Add, BF_One, BF_InverseSourceAlpha, BO_Add, BF_Zero, BF_One >::GetRHI(); } }; /** Specialization for ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Specular */ class FMaterialPolicy_BaseColorNormalSpecular { private: /** Compile time helper to build blend state from the connected output attribute mask. */ static constexpr EColorWriteMask GetColorMaskFromAttributeMask(uint8 AttributeMask, uint8 RenderTargetIndex) { // Color mask in the output render targets for each of the relevant attributes in ERuntimeVirtualTextureAttributeType const EColorWriteMask AttributeMasks[][3] = { { CW_RGBA, CW_NONE, CW_NONE }, // BaseColor { CW_NONE, EColorWriteMask(CW_RED | CW_GREEN | CW_ALPHA), EColorWriteMask(CW_BLUE | CW_ALPHA) }, // Normal { CW_NONE, CW_NONE, EColorWriteMask(CW_GREEN | CW_ALPHA) }, // Roughness { CW_NONE, CW_NONE, EColorWriteMask(CW_RED | CW_ALPHA) }, // Specular { CW_NONE, EColorWriteMask(CW_BLUE | CW_ALPHA), CW_NONE }, // Mask }; // Combine the color masks for this AttributeMask EColorWriteMask ColorWriteMask = CW_NONE; for (int32 i = 0; i < 5; ++i) { if (AttributeMask & (1 << i)) { ColorWriteMask = EColorWriteMask(ColorWriteMask | AttributeMasks[i][RenderTargetIndex]); } } return ColorWriteMask; } /** Helper to convert the connected output attribute mask to a blend state with a color mask for these attributes. */ template< uint32 AttributeMask > static FRHIBlendState* TGetBlendStateFromAttributeMask() { return TStaticBlendState< GetColorMaskFromAttributeMask(AttributeMask, 0), BO_Add, BF_One, BF_InverseSourceAlpha, BO_Add, BF_Zero, BF_One, GetColorMaskFromAttributeMask(AttributeMask, 1), BO_Add, BF_One, BF_InverseSourceAlpha, BO_Add, BF_Zero, BF_One, GetColorMaskFromAttributeMask(AttributeMask, 2), BO_Add, BF_One, BF_InverseSourceAlpha, BO_Add, BF_Zero, BF_One >::GetRHI(); } /** Runtime conversion of attribute mask to static blend state. */ static FRHIBlendState* GetBlendStateImpl(uint8 AttributeMask) { // We have 5 relevant bits in the attribute mask. Any more and this would get painful... switch (AttributeMask & 0x1f) { case 1: return TGetBlendStateFromAttributeMask<1>(); case 2: return TGetBlendStateFromAttributeMask<2>(); case 3: return TGetBlendStateFromAttributeMask<3>(); case 4: return TGetBlendStateFromAttributeMask<4>(); case 5: return TGetBlendStateFromAttributeMask<5>(); case 6: return TGetBlendStateFromAttributeMask<6>(); case 7: return TGetBlendStateFromAttributeMask<7>(); case 8: return TGetBlendStateFromAttributeMask<8>(); case 9: return TGetBlendStateFromAttributeMask<9>(); case 10: return TGetBlendStateFromAttributeMask<10>(); case 11: return TGetBlendStateFromAttributeMask<11>(); case 12: return TGetBlendStateFromAttributeMask<12>(); case 13: return TGetBlendStateFromAttributeMask<13>(); case 14: return TGetBlendStateFromAttributeMask<14>(); case 15: return TGetBlendStateFromAttributeMask<15>(); case 16: return TGetBlendStateFromAttributeMask<16>(); case 17: return TGetBlendStateFromAttributeMask<17>(); case 18: return TGetBlendStateFromAttributeMask<18>(); case 19: return TGetBlendStateFromAttributeMask<19>(); case 20: return TGetBlendStateFromAttributeMask<20>(); case 21: return TGetBlendStateFromAttributeMask<21>(); case 22: return TGetBlendStateFromAttributeMask<22>(); case 23: return TGetBlendStateFromAttributeMask<23>(); case 24: return TGetBlendStateFromAttributeMask<24>(); case 25: return TGetBlendStateFromAttributeMask<25>(); case 26: return TGetBlendStateFromAttributeMask<26>(); case 27: return TGetBlendStateFromAttributeMask<27>(); case 28: return TGetBlendStateFromAttributeMask<28>(); case 29: return TGetBlendStateFromAttributeMask<29>(); case 30: return TGetBlendStateFromAttributeMask<30>(); default: return TGetBlendStateFromAttributeMask<31>(); } } public: static bool ShouldCompilePermutation(const FMeshMaterialShaderPermutationParameters& Parameters) { return RuntimeVirtualTexture::IsMaterialTypeSupported(ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Specular, Parameters.Platform); } static void ModifyCompilationEnvironment(FShaderCompilerEnvironment& OutEnvironment) { OutEnvironment.SetDefine(TEXT("OUT_BASECOLOR_NORMAL_SPECULAR"), 1); } static FRHIBlendState* GetBlendState(uint8 OutputAttributeMask) { return GetBlendStateImpl(OutputAttributeMask); } }; /** Specialization for ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Roughness */ class FMaterialPolicy_BaseColorNormalRoughness { private: /** Compile time helper to build blend state from the connected output attribute mask. */ static constexpr EColorWriteMask GetColorMaskFromAttributeMask(uint8 AttributeMask, uint8 RenderTargetIndex) { // Color mask in the output render targets for each of the relevant attributes in ERuntimeVirtualTextureAttributeType const EColorWriteMask AttributeMasks[][2] = { { CW_RGBA, CW_NONE}, // BaseColor { CW_NONE, EColorWriteMask(CW_RED| CW_BLUE | CW_ALPHA)}, // Normal { CW_NONE, EColorWriteMask(CW_GREEN | CW_ALPHA)}, // Roughness { CW_NONE, CW_NONE}, // Specular { CW_NONE, CW_NONE}, // Mask }; // Combine the color masks for this AttributeMask EColorWriteMask ColorWriteMask = CW_NONE; for (int32 i = 0; i < 5; ++i) { if (AttributeMask & (1 << i)) { ColorWriteMask = EColorWriteMask(ColorWriteMask | AttributeMasks[i][RenderTargetIndex]); } } return ColorWriteMask; } /** Helper to convert the connected output attribute mask to a blend state with a color mask for these attributes. */ template< uint32 AttributeMask > static FRHIBlendState* TGetBlendStateFromAttributeMask() { return TStaticBlendState< GetColorMaskFromAttributeMask(AttributeMask, 0), BO_Add, BF_One, BF_InverseSourceAlpha, BO_Add, BF_Zero, BF_One, // normal XY is stored in R and B channels, and the Sign of Z is considered always positive GetColorMaskFromAttributeMask(AttributeMask, 1), BO_Add, BF_One, BF_InverseSourceAlpha, BO_Add, BF_Zero, BF_One>::GetRHI(); } /** Runtime conversion of attribute mask to static blend state. */ static FRHIBlendState* GetBlendStateImpl(uint8 AttributeMask) { // We have 5 relevant bits in the attribute mask. Any more and this would get painful... switch (AttributeMask & 0x1f) { case 1: return TGetBlendStateFromAttributeMask<1>(); case 2: return TGetBlendStateFromAttributeMask<2>(); case 3: return TGetBlendStateFromAttributeMask<3>(); case 4: return TGetBlendStateFromAttributeMask<4>(); case 5: return TGetBlendStateFromAttributeMask<5>(); case 6: return TGetBlendStateFromAttributeMask<6>(); case 7: return TGetBlendStateFromAttributeMask<7>(); case 8: return TGetBlendStateFromAttributeMask<8>(); case 9: return TGetBlendStateFromAttributeMask<9>(); case 10: return TGetBlendStateFromAttributeMask<10>(); case 11: return TGetBlendStateFromAttributeMask<11>(); case 12: return TGetBlendStateFromAttributeMask<12>(); case 13: return TGetBlendStateFromAttributeMask<13>(); case 14: return TGetBlendStateFromAttributeMask<14>(); case 15: return TGetBlendStateFromAttributeMask<15>(); case 16: return TGetBlendStateFromAttributeMask<16>(); case 17: return TGetBlendStateFromAttributeMask<17>(); case 18: return TGetBlendStateFromAttributeMask<18>(); case 19: return TGetBlendStateFromAttributeMask<19>(); case 20: return TGetBlendStateFromAttributeMask<20>(); case 21: return TGetBlendStateFromAttributeMask<21>(); case 22: return TGetBlendStateFromAttributeMask<22>(); case 23: return TGetBlendStateFromAttributeMask<23>(); case 24: return TGetBlendStateFromAttributeMask<24>(); case 25: return TGetBlendStateFromAttributeMask<25>(); case 26: return TGetBlendStateFromAttributeMask<26>(); case 27: return TGetBlendStateFromAttributeMask<27>(); case 28: return TGetBlendStateFromAttributeMask<28>(); case 29: return TGetBlendStateFromAttributeMask<29>(); case 30: return TGetBlendStateFromAttributeMask<30>(); default: return TGetBlendStateFromAttributeMask<31>(); } } public: static bool ShouldCompilePermutation(const FMeshMaterialShaderPermutationParameters& Parameters) { return RuntimeVirtualTexture::IsMaterialTypeSupported(ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Roughness, Parameters.Platform); } static void ModifyCompilationEnvironment(FShaderCompilerEnvironment& OutEnvironment) { OutEnvironment.SetDefine(TEXT("OUT_BASECOLOR_NORMAL_ROUGHNESS"), 1); } static FRHIBlendState* GetBlendState(uint8 OutputAttributeMask) { return GetBlendStateImpl(OutputAttributeMask); } }; /** Specialization for ERuntimeVirtualTextureMaterialType::Mask4 */ class FMaterialPolicy_Mask4 { public: static bool ShouldCompilePermutation(const FMeshMaterialShaderPermutationParameters& Parameters) { return RuntimeVirtualTexture::IsMaterialTypeSupported(ERuntimeVirtualTextureMaterialType::Mask4, Parameters.Platform); } static void ModifyCompilationEnvironment(FShaderCompilerEnvironment& OutEnvironment) { OutEnvironment.SetDefine(TEXT("OUT_MASK4"), 1); } static FRHIBlendState* GetBlendState(uint8 OutputAttributeMask) { return TStaticBlendState< CW_RGBA, BO_Add, BF_One, BF_InverseSourceAlpha, BO_Add, BF_Zero, BF_One, CW_RED, BO_Add, BF_One, BF_InverseSourceAlpha, BO_Add, BF_Zero, BF_One >::GetRHI(); } }; /** Specialization for ERuntimeVirtualTextureMaterialType::WorldHeight */ class FMaterialPolicy_WorldHeight { public: static bool ShouldCompilePermutation(const FMeshMaterialShaderPermutationParameters& Parameters) { return RuntimeVirtualTexture::IsMaterialTypeSupported(ERuntimeVirtualTextureMaterialType::WorldHeight, Parameters.Platform); } static void ModifyCompilationEnvironment(FShaderCompilerEnvironment& OutEnvironment) { OutEnvironment.SetDefine(TEXT("OUT_WORLDHEIGHT"), 1); OutEnvironment.SetRenderTargetOutputFormat(0, PF_R32_FLOAT); } static FRHIBlendState* GetBlendState(uint8 OutputAttributeMask) { return TStaticBlendState< CW_RED, BO_Max, BF_One, BF_One, BO_Add, BF_One, BF_One >::GetRHI(); } }; /** Specialization for ERuntimeVirtualTextureMaterialType::Displacement */ class FMaterialPolicy_Displacement { public: static bool ShouldCompilePermutation(const FMeshMaterialShaderPermutationParameters& Parameters) { return RuntimeVirtualTexture::IsMaterialTypeSupported(ERuntimeVirtualTextureMaterialType::Displacement, Parameters.Platform); } static void ModifyCompilationEnvironment(FShaderCompilerEnvironment& OutEnvironment) { OutEnvironment.SetDefine(TEXT("OUT_DISPLACEMENT"), 1); OutEnvironment.SetRenderTargetOutputFormat(0, PF_A32B32G32R32F); } static FRHIBlendState* GetBlendState(uint8 OutputAttributeMask) { return TStaticBlendState< CW_RED, BO_Add, BF_One, BF_InverseSourceAlpha, BO_Add, BF_Zero, BF_One >::GetRHI(); } }; /** Vertex shader derivation of material shader. Templated on policy for virtual texture layout. */ template< class MaterialPolicy > class FShader_VirtualTextureMaterialDraw_VS : public FShader_VirtualTextureMaterialDraw { public: DECLARE_SHADER_TYPE(FShader_VirtualTextureMaterialDraw_VS, MeshMaterial); FShader_VirtualTextureMaterialDraw_VS() {} FShader_VirtualTextureMaterialDraw_VS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) : FShader_VirtualTextureMaterialDraw(Initializer) {} static bool ShouldCompilePermutation(const FMeshMaterialShaderPermutationParameters& Parameters) { return FShader_VirtualTextureMaterialDraw::ShouldCompilePermutation(Parameters) && MaterialPolicy::ShouldCompilePermutation(Parameters); } static void ModifyCompilationEnvironment(const FMaterialShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FShader_VirtualTextureMaterialDraw::ModifyCompilationEnvironment(Parameters, OutEnvironment); MaterialPolicy::ModifyCompilationEnvironment(OutEnvironment); } }; /** Pixel shader derivation of material shader. Templated on policy for virtual texture layout. */ template< class MaterialPolicy > class FShader_VirtualTextureMaterialDraw_PS : public FShader_VirtualTextureMaterialDraw { public: DECLARE_SHADER_TYPE(FShader_VirtualTextureMaterialDraw_PS< MaterialPolicy >, MeshMaterial); FShader_VirtualTextureMaterialDraw_PS() {} FShader_VirtualTextureMaterialDraw_PS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) : FShader_VirtualTextureMaterialDraw(Initializer) {} static void ModifyCompilationEnvironment(const FMaterialShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FShader_VirtualTextureMaterialDraw::ModifyCompilationEnvironment(Parameters, OutEnvironment); MaterialPolicy::ModifyCompilationEnvironment(OutEnvironment); } }; // If we change this macro or add additional policy types then we need to update GetRuntimeVirtualTextureShaderTypes() in LandscapeRender.cpp // That code is used to filter out unnecessary shader variations #define IMPLEMENT_VIRTUALTEXTURE_SHADER_TYPE(PolicyType, PolicyName) \ typedef FShader_VirtualTextureMaterialDraw_VS TVirtualTextureVS##PolicyName; \ IMPLEMENT_MATERIAL_SHADER_TYPE(template<>,TVirtualTextureVS##PolicyName, TEXT("/Engine/Private/VirtualTextureMaterial.usf"), TEXT("MainVS"), SF_Vertex); \ typedef FShader_VirtualTextureMaterialDraw_PS TVirtualTexturePS##PolicyName; \ IMPLEMENT_MATERIAL_SHADER_TYPE(template<>,TVirtualTexturePS##PolicyName, TEXT("/Engine/Private/VirtualTextureMaterial.usf"), TEXT("MainPS"), SF_Pixel); IMPLEMENT_VIRTUALTEXTURE_SHADER_TYPE(FMaterialPolicy_BaseColor, BaseColor); IMPLEMENT_VIRTUALTEXTURE_SHADER_TYPE(FMaterialPolicy_BaseColorNormalRoughness, BaseColorNormalRoughness); IMPLEMENT_VIRTUALTEXTURE_SHADER_TYPE(FMaterialPolicy_BaseColorNormalSpecular, BaseColorNormalSpecular); IMPLEMENT_VIRTUALTEXTURE_SHADER_TYPE(FMaterialPolicy_Mask4, Mask4); IMPLEMENT_VIRTUALTEXTURE_SHADER_TYPE(FMaterialPolicy_WorldHeight, WorldHeight); IMPLEMENT_VIRTUALTEXTURE_SHADER_TYPE(FMaterialPolicy_Displacement, Displacement); /** Structure to localize the setup of our render graph based on the virtual texture setup. */ struct FRenderGraphSetup { static void SetupRenderTargetsInfo(ERuntimeVirtualTextureMaterialType MaterialType, ERHIFeatureLevel::Type FeatureLevel, bool bLQFormat, FGraphicsPipelineRenderTargetsInfo& RenderTargetsInfo) { const ETextureCreateFlags RTCreateFlags = TexCreate_RenderTargetable | TexCreate_ShaderResource; const ETextureCreateFlags RTSrgbFlags = TexCreate_SRGB; switch (MaterialType) { case ERuntimeVirtualTextureMaterialType::BaseColor: AddRenderTargetInfo(PF_B8G8R8A8, RTCreateFlags | RTSrgbFlags, RenderTargetsInfo); break; case ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Roughness: AddRenderTargetInfo(bLQFormat ? PF_R5G6B5_UNORM : PF_B8G8R8A8, RTCreateFlags, RenderTargetsInfo); AddRenderTargetInfo(bLQFormat ? PF_R5G6B5_UNORM : PF_B8G8R8A8, RTCreateFlags, RenderTargetsInfo); break; case ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Specular: AddRenderTargetInfo(PF_B8G8R8A8, RTCreateFlags | RTSrgbFlags, RenderTargetsInfo); AddRenderTargetInfo(PF_B8G8R8A8, RTCreateFlags, RenderTargetsInfo); AddRenderTargetInfo(PF_B8G8R8A8, RTCreateFlags, RenderTargetsInfo); break; case ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Specular_YCoCg: AddRenderTargetInfo(PF_B8G8R8A8, RTCreateFlags | RTSrgbFlags, RenderTargetsInfo); AddRenderTargetInfo(PF_B8G8R8A8, RTCreateFlags, RenderTargetsInfo); AddRenderTargetInfo(PF_B8G8R8A8, RTCreateFlags, RenderTargetsInfo); break; case ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Specular_Mask_YCoCg: AddRenderTargetInfo(PF_B8G8R8A8, RTCreateFlags | RTSrgbFlags, RenderTargetsInfo); AddRenderTargetInfo(PF_B8G8R8A8, RTCreateFlags, RenderTargetsInfo); AddRenderTargetInfo(PF_B8G8R8A8, RTCreateFlags, RenderTargetsInfo); break; case ERuntimeVirtualTextureMaterialType::Mask4: AddRenderTargetInfo(PF_B8G8R8A8, RTCreateFlags, RenderTargetsInfo); AddRenderTargetInfo(PF_B8G8R8A8, RTCreateFlags, RenderTargetsInfo); break; case ERuntimeVirtualTextureMaterialType::WorldHeight: case ERuntimeVirtualTextureMaterialType::Displacement: AddRenderTargetInfo(PF_G16, RTCreateFlags, RenderTargetsInfo); break; } } /** Initializer description for the graph setup. */ struct FInitDesc { ERHIFeatureLevel::Type FeatureLevel; ERuntimeVirtualTextureMaterialType MaterialType; FIntPoint TextureSize; int32 PageCount = 1; TArray> OutputTargets; bool bClearTextures = false; bool bIsThumbnails = false; /** Initialize from a page batch description. */ FInitDesc(FRenderPageBatchDesc const& InDesc) { check(InDesc.SceneRenderer != nullptr && InDesc.SceneRenderer->GetScene() != nullptr); FeatureLevel = InDesc.SceneRenderer->GetScene()->GetFeatureLevel(); MaterialType = InDesc.MaterialType; PageCount = InDesc.NumPageDescs; TextureSize = InDesc.PageDescs[0].DestRect[0].Size(); OutputTargets.Add(InDesc.Targets[0].PooledRenderTarget); OutputTargets.Add(InDesc.Targets[1].PooledRenderTarget); OutputTargets.Add(InDesc.Targets[2].PooledRenderTarget); bClearTextures = InDesc.bClearTextures; bIsThumbnails = InDesc.bIsThumbnails; } }; /** CreateTextureDesc() creates a texture2Darray if we have page batch size > 1 or a simple texture2D otherwise. */ static FRDGTextureDesc CreateTextureDesc(FIntPoint Size, EPixelFormat Format, FClearValueBinding ClearValue, ETextureCreateFlags Flags, uint16 ArraySize) { if (ArraySize > 1) { return FRDGTextureDesc::Create2DArray(Size, Format, ClearValue, Flags | TexCreate_TargetArraySlicesIndependently, ArraySize); } else { return FRDGTextureDesc::Create2D(Size, Format, ClearValue, Flags); } } /** CreateTextureSRV() createss an SRV for a single slice if Texture is a texture array. */ static FRDGTextureSRVRef CreateTextureSRV(FRDGBuilder& GraphBuilder, FRDGTextureRef Texture, int32 ArraySlice) { if (Texture == nullptr) { return nullptr; } if (ArraySlice >= 0) { return GraphBuilder.CreateSRV(FRDGTextureSRVDesc::CreateForSlice(Texture, ArraySlice)); } return GraphBuilder.CreateSRV(Texture); } /** Initialize the graph setup. */ void Init(FRDGBuilder& GraphBuilder, FInitDesc const& Desc) { const EPixelFormat OutputFormat0 = Desc.OutputTargets[0].IsValid() ? Desc.OutputTargets[0]->GetRHI()->GetFormat() : PF_Unknown; bRenderPass = OutputFormat0 != PF_Unknown; bCopyThumbnailPass = bRenderPass && Desc.bIsThumbnails; const bool bCompressedFormat = GPixelFormats[OutputFormat0].BlockSizeX == 4 && GPixelFormats[OutputFormat0].BlockSizeY == 4; const bool bLQFormat = OutputFormat0 == PF_R5G6B5_UNORM; bCompressPass = bRenderPass && !bCopyThumbnailPass && bCompressedFormat; bCopyPass = bRenderPass && !bCopyThumbnailPass && !bCompressPass && (Desc.MaterialType == ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Specular || Desc.MaterialType == ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Specular_YCoCg || Desc.MaterialType == ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Specular_Mask_YCoCg || Desc.MaterialType == ERuntimeVirtualTextureMaterialType::Mask4); // Use direct aliasing for compression pass on platforms that support it. bDirectAliasing = bCompressedFormat && GRHISupportsUAVFormatAliasing && CVarVTDirectCompress.GetValueOnRenderThread() != 0; // ForceImmediateFirstBarrier so that UAV transitions for the output targets aren't hoisted above // Finalize() and into RenderFinalize() or earlier where they will be incorrect for virtual texture sampling. const ERDGTextureFlags ExternalTextureFlags = ERDGTextureFlags::ForceImmediateFirstBarrier; // Some problems happen when we don't use ERenderTargetLoadAction::EClear: // * Some RHI need explicit flag to avoid a fast clear (TexCreate_NoFastClear). // * DX12 RHI has a bug with RDG transient allocator (UE-173023) so we use TexCreate_Shared to avoid that. const ETextureCreateFlags RTNoClearHackFlags = TexCreate_NoFastClear | TexCreate_Shared; const ETextureCreateFlags RTClearFlags = Desc.bClearTextures ? TexCreate_None : RTNoClearHackFlags; const ETextureCreateFlags RTCreateFlags = TexCreate_RenderTargetable | TexCreate_ShaderResource | RTClearFlags; const ETextureCreateFlags RTSrgbFlags = TexCreate_SRGB; const EPixelFormat Compressed64BitFormat = UseRGBA16(GMaxRHIShaderPlatform) ? PF_R16G16B16A16_UINT : PF_R32G32_UINT; const EPixelFormat Compressed128BitFormat = PF_R32G32B32A32_UINT; const int32 PageCount = Desc.PageCount; switch (Desc.MaterialType) { case ERuntimeVirtualTextureMaterialType::BaseColor: if (bRenderPass) { OutputAlias0 = RenderTexture0 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize, PF_B8G8R8A8, FClearValueBinding::Black, RTCreateFlags | RTSrgbFlags, PageCount), TEXT("RenderTexture0")); } if (bCompressPass) { if (bDirectAliasing) { CompressTexture0 = GraphBuilder.RegisterExternalTexture(Desc.OutputTargets[0], ExternalTextureFlags); CompressTextureUAV0_64bit = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(CompressTexture0, 0, Compressed64BitFormat)); OutputAlias0 = nullptr; } else { OutputAlias0 = CompressTexture0 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize / 4, Compressed64BitFormat, FClearValueBinding::None, TexCreate_UAV, PageCount), TEXT("CompressTexture0")); CompressTextureUAV0_64bit = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(CompressTexture0)); } } if (bCopyThumbnailPass) { OutputAlias0 = CopyTexture0 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize, PF_B8G8R8A8, FClearValueBinding::Black, RTCreateFlags | RTSrgbFlags, PageCount), TEXT("CopyTexture0")); } break; case ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Roughness: if (bRenderPass) { OutputAlias0 = RenderTexture0 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize, bLQFormat ? PF_R5G6B5_UNORM : PF_B8G8R8A8, FClearValueBinding::Black, RTCreateFlags, PageCount), TEXT("RenderTexture0")); OutputAlias1 = RenderTexture1 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize, bLQFormat ? PF_R5G6B5_UNORM : PF_B8G8R8A8, FClearValueBinding::Black, RTCreateFlags, PageCount), TEXT("RenderTexture1")); } if (bCompressPass) { if (bDirectAliasing) { CompressTexture0 = GraphBuilder.RegisterExternalTexture(Desc.OutputTargets[0], ExternalTextureFlags); CompressTextureUAV0_64bit = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(CompressTexture0, 0, Compressed64BitFormat)); CompressTexture1 = GraphBuilder.RegisterExternalTexture(Desc.OutputTargets[1], ExternalTextureFlags); CompressTextureUAV1_128bit = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(CompressTexture1, 0, Compressed128BitFormat)); OutputAlias0 = OutputAlias1 = nullptr; } else { OutputAlias0 = CompressTexture0 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize / 4, Compressed64BitFormat, FClearValueBinding::None, TexCreate_UAV, PageCount), TEXT("CompressTexture0")); CompressTextureUAV0_64bit = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(CompressTexture0)); OutputAlias1 = CompressTexture1 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize / 4, Compressed128BitFormat, FClearValueBinding::None, TexCreate_UAV, PageCount), TEXT("CompressTexture1")); CompressTextureUAV1_128bit = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(CompressTexture1)); } } if (bCopyThumbnailPass) { OutputAlias0 = CopyTexture0 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize, bLQFormat ? PF_R5G6B5_UNORM : PF_B8G8R8A8, FClearValueBinding::Black, RTCreateFlags, PageCount), TEXT("CopyTexture0")); OutputAlias1 = nullptr; } break; case ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Specular: if (bRenderPass) { OutputAlias0 = RenderTexture0 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize, PF_B8G8R8A8, FClearValueBinding::Black, RTCreateFlags | RTSrgbFlags, PageCount), TEXT("RenderTexture0")); RenderTexture1 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize, PF_B8G8R8A8, FClearValueBinding::DefaultNormal8Bit, RTCreateFlags, PageCount), TEXT("RenderTexture1")); RenderTexture2 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize, PF_B8G8R8A8, FClearValueBinding::DefaultNormal8Bit, RTCreateFlags, PageCount), TEXT("RenderTexture2")); } if (bCompressPass) { if (bDirectAliasing) { CompressTexture0 = GraphBuilder.RegisterExternalTexture(Desc.OutputTargets[0], ExternalTextureFlags); CompressTextureUAV0_128bit = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(CompressTexture0, 0, Compressed128BitFormat)); CompressTexture1 = GraphBuilder.RegisterExternalTexture(Desc.OutputTargets[1], ExternalTextureFlags); CompressTextureUAV1_128bit = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(CompressTexture1, 0, Compressed128BitFormat)); OutputAlias0 = OutputAlias1 = nullptr; } else { OutputAlias0 = CompressTexture0 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize / 4, Compressed128BitFormat, FClearValueBinding::None, TexCreate_UAV, PageCount), TEXT("CompressTexture0")); CompressTextureUAV0_128bit = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(CompressTexture0)); OutputAlias1 = CompressTexture1 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize / 4, Compressed128BitFormat, FClearValueBinding::None, TexCreate_UAV, PageCount), TEXT("CompressTexture1")); CompressTextureUAV1_128bit = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(CompressTexture1)); } } if (bCopyPass) { OutputAlias0 = CopyTexture0 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize, PF_B8G8R8A8, FClearValueBinding::Black, RTCreateFlags | RTSrgbFlags, PageCount), TEXT("CopyTexture0")); OutputAlias1 = CopyTexture1 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize, PF_B8G8R8A8, FClearValueBinding::Black, RTCreateFlags, PageCount), TEXT("CopyTexture1")); } if (bCopyThumbnailPass) { OutputAlias0 = CopyTexture0 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize, PF_B8G8R8A8, FClearValueBinding::Black, RTCreateFlags | RTSrgbFlags, PageCount), TEXT("CopyTexture0")); } break; case ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Specular_YCoCg: if (bRenderPass) { OutputAlias0 = RenderTexture0 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize, PF_B8G8R8A8, FClearValueBinding::Black, RTCreateFlags | RTSrgbFlags, PageCount), TEXT("RenderTexture0")); RenderTexture1 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize, PF_B8G8R8A8, FClearValueBinding::DefaultNormal8Bit, RTCreateFlags, PageCount), TEXT("RenderTexture1")); RenderTexture2 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize, PF_B8G8R8A8, FClearValueBinding::DefaultNormal8Bit, RTCreateFlags, PageCount), TEXT("RenderTexture2")); } if (bCompressPass) { if (bDirectAliasing) { CompressTexture0 = GraphBuilder.RegisterExternalTexture(Desc.OutputTargets[0], ExternalTextureFlags); CompressTextureUAV0_128bit = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(CompressTexture0, 0, Compressed128BitFormat)); CompressTexture1 = GraphBuilder.RegisterExternalTexture(Desc.OutputTargets[1], ExternalTextureFlags); CompressTextureUAV1_128bit = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(CompressTexture1, 0, Compressed128BitFormat)); CompressTexture2 = GraphBuilder.RegisterExternalTexture(Desc.OutputTargets[2], ExternalTextureFlags); CompressTextureUAV2_64bit = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(CompressTexture2, 0, Compressed64BitFormat)); OutputAlias0 = OutputAlias1 = OutputAlias2 = nullptr; } else { OutputAlias0 = CompressTexture0 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize / 4, Compressed128BitFormat, FClearValueBinding::None, TexCreate_UAV, PageCount), TEXT("CompressTexture0")); CompressTextureUAV0_128bit = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(CompressTexture0)); OutputAlias1 = CompressTexture1 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize / 4, Compressed128BitFormat, FClearValueBinding::None, TexCreate_UAV, PageCount), TEXT("CompressTexture1")); CompressTextureUAV1_128bit = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(CompressTexture1)); OutputAlias2 = CompressTexture2 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize / 4, Compressed64BitFormat, FClearValueBinding::None, TexCreate_UAV, PageCount), TEXT("CompressTexture2")); CompressTextureUAV2_64bit = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(CompressTexture2)); } } if (bCopyPass) { OutputAlias0 = CopyTexture0 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize, PF_B8G8R8A8, FClearValueBinding::Black, RTCreateFlags, PageCount), TEXT("CopyTexture0")); OutputAlias1 = CopyTexture1 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize, PF_B8G8R8A8, FClearValueBinding::Black, RTCreateFlags, PageCount), TEXT("CopyTexture1")); OutputAlias2 = CopyTexture2 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize, PF_B8G8R8A8, FClearValueBinding::Black, RTCreateFlags, PageCount), TEXT("CopyTexture2")); } if (bCopyThumbnailPass) { OutputAlias0 = CopyTexture0 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize, PF_B8G8R8A8, FClearValueBinding::Black, RTCreateFlags | RTSrgbFlags, PageCount), TEXT("CopyTexture0")); } break; case ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Specular_Mask_YCoCg: if (bRenderPass) { OutputAlias0 = RenderTexture0 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize, PF_B8G8R8A8, FClearValueBinding::Black, RTCreateFlags | RTSrgbFlags, PageCount), TEXT("RenderTexture0")); RenderTexture1 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize, PF_B8G8R8A8, FClearValueBinding::DefaultNormal8Bit, RTCreateFlags, PageCount), TEXT("RenderTexture1")); RenderTexture2 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize, PF_B8G8R8A8, FClearValueBinding::DefaultNormal8Bit, RTCreateFlags, PageCount), TEXT("RenderTexture2")); } if (bCompressPass) { if (bDirectAliasing) { CompressTexture0 = GraphBuilder.RegisterExternalTexture(Desc.OutputTargets[0], ExternalTextureFlags); CompressTextureUAV0_128bit = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(CompressTexture0, 0, Compressed128BitFormat)); CompressTexture1 = GraphBuilder.RegisterExternalTexture(Desc.OutputTargets[1], ExternalTextureFlags); CompressTextureUAV1_128bit = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(CompressTexture1, 0, Compressed128BitFormat)); CompressTexture2 = GraphBuilder.RegisterExternalTexture(Desc.OutputTargets[2], ExternalTextureFlags); CompressTextureUAV2_128bit = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(CompressTexture2, 0, Compressed128BitFormat)); OutputAlias0 = OutputAlias1 = OutputAlias2 = nullptr; } else { OutputAlias0 = CompressTexture0 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize / 4, Compressed128BitFormat, FClearValueBinding::None, TexCreate_UAV, PageCount), TEXT("CompressTexture0")); CompressTextureUAV0_128bit = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(CompressTexture0)); OutputAlias1 = CompressTexture1 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize / 4, Compressed128BitFormat, FClearValueBinding::None, TexCreate_UAV, PageCount), TEXT("CompressTexture1")); CompressTextureUAV1_128bit = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(CompressTexture1)); OutputAlias2 = CompressTexture2 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize / 4, Compressed128BitFormat, FClearValueBinding::None, TexCreate_UAV, PageCount), TEXT("CompressTexture2")); CompressTextureUAV2_128bit = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(CompressTexture2)); } } if (bCopyPass) { OutputAlias0 = CopyTexture0 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize, PF_B8G8R8A8, FClearValueBinding::Black, RTCreateFlags, PageCount), TEXT("CopyTexture0")); OutputAlias1 = CopyTexture1 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize, PF_B8G8R8A8, FClearValueBinding::Black, RTCreateFlags, PageCount), TEXT("CopyTexture1")); OutputAlias2 = CopyTexture2 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize, PF_B8G8R8A8, FClearValueBinding::Black, RTCreateFlags, PageCount), TEXT("CopyTexture2")); } if (bCopyThumbnailPass) { OutputAlias0 = CopyTexture0 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize, PF_B8G8R8A8, FClearValueBinding::Black, RTCreateFlags | RTSrgbFlags, PageCount), TEXT("CopyTexture0")); } break; case ERuntimeVirtualTextureMaterialType::Mask4: if (bRenderPass) { OutputAlias0 = RenderTexture0 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize, PF_B8G8R8A8, FClearValueBinding::Black, RTCreateFlags, PageCount), TEXT("RenderTexture0")); OutputAlias1 = RenderTexture1 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize, PF_B8G8R8A8, FClearValueBinding::Black, RTCreateFlags, PageCount), TEXT("RenderTexture1")); } if (bCompressPass) { if (bDirectAliasing) { CompressTexture0 = GraphBuilder.RegisterExternalTexture(Desc.OutputTargets[0], ExternalTextureFlags); CompressTextureUAV0_128bit = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(CompressTexture0, 0, Compressed128BitFormat)); OutputAlias0 = nullptr; } else { OutputAlias0 = CompressTexture0 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize / 4, Compressed128BitFormat, FClearValueBinding::None, TexCreate_UAV, PageCount), TEXT("CompressTexture0")); CompressTextureUAV0_128bit = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(CompressTexture0)); } } if (bCopyPass || bCopyThumbnailPass) { OutputAlias0 = CopyTexture0 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize, PF_B8G8R8A8, FClearValueBinding::Black, RTCreateFlags, PageCount), TEXT("CopyTexture0")); } break; case ERuntimeVirtualTextureMaterialType::WorldHeight: if (bRenderPass) { OutputAlias0 = RenderTexture0 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize, PF_G16, FClearValueBinding::Black, RTCreateFlags, PageCount), TEXT("RenderTexture0")); } if (bCopyThumbnailPass) { OutputAlias0 = CopyTexture0 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize, PF_B8G8R8A8, FClearValueBinding::Black, RTCreateFlags, PageCount), TEXT("CopyTexture0")); } break; case ERuntimeVirtualTextureMaterialType::Displacement: if (bRenderPass) { OutputAlias0 = RenderTexture0 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize, PF_G16, FClearValueBinding::Black, RTCreateFlags, PageCount), TEXT("RenderTexture0")); } if (bCompressPass) { if (bDirectAliasing) { CompressTexture0 = GraphBuilder.RegisterExternalTexture(Desc.OutputTargets[0], ExternalTextureFlags); CompressTextureUAV0_64bit = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(CompressTexture0, 0, Compressed64BitFormat)); OutputAlias0 = nullptr; } else { OutputAlias0 = CompressTexture0 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize / 4, Compressed64BitFormat, FClearValueBinding::None, TexCreate_UAV, PageCount), TEXT("CompressTexture0")); CompressTextureUAV0_64bit = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(CompressTexture0)); } } if (bCopyThumbnailPass) { OutputAlias0 = CopyTexture0 = GraphBuilder.CreateTexture(CreateTextureDesc(Desc.TextureSize, PF_B8G8R8A8, FClearValueBinding::Black, RTCreateFlags, PageCount), TEXT("CopyTexture0")); } break; } if (OutputAlias0 && Desc.OutputTargets[0]) { TargetTexture0 = GraphBuilder.RegisterExternalTexture(Desc.OutputTargets[0], ExternalTextureFlags); } if (OutputAlias1 && Desc.OutputTargets[1]) { TargetTexture1 = GraphBuilder.RegisterExternalTexture(Desc.OutputTargets[1], ExternalTextureFlags); } if (OutputAlias2 && Desc.OutputTargets[2]) { TargetTexture2 = GraphBuilder.RegisterExternalTexture(Desc.OutputTargets[2], ExternalTextureFlags); } } /** Flags to express what passes we need for this virtual texture layout. */ bool bRenderPass = false; bool bCompressPass = false; bool bCopyPass = false; bool bCopyThumbnailPass = false; bool bDirectAliasing = false; /** Render graph textures needed for this virtual texture layout. */ FRDGTextureRef RenderTexture0 = nullptr; FRDGTextureRef RenderTexture1 = nullptr; FRDGTextureRef RenderTexture2 = nullptr; FRDGTextureRef CompressTexture0 = nullptr; FRDGTextureRef CompressTexture1 = nullptr; FRDGTextureRef CompressTexture2 = nullptr; FRDGTextureUAVRef CompressTextureUAV0_64bit = nullptr; FRDGTextureUAVRef CompressTextureUAV1_64bit = nullptr; FRDGTextureUAVRef CompressTextureUAV2_64bit = nullptr; FRDGTextureUAVRef CompressTextureUAV0_128bit = nullptr; FRDGTextureUAVRef CompressTextureUAV1_128bit = nullptr; FRDGTextureUAVRef CompressTextureUAV2_128bit = nullptr; FRDGTextureRef CopyTexture0 = nullptr; FRDGTextureRef CopyTexture1 = nullptr; FRDGTextureRef CopyTexture2 = nullptr; /** Aliases to one of the render/compress/copy textures. This is what we will Copy into the final physical texture. */ FRDGTextureRef OutputAlias0 = nullptr; FRDGTextureRef OutputAlias1 = nullptr; FRDGTextureRef OutputAlias2 = nullptr; /** If we have output aliases, then these will containg the final physical texture targets. */ FRDGTextureRef TargetTexture0 = nullptr; FRDGTextureRef TargetTexture1 = nullptr; FRDGTextureRef TargetTexture2 = nullptr; }; /** * Context for rendering a batch of pages. * Holds the batch description and the render graph allocations. * Allows us to maintain state across RenderFinalize() and Finalize() calls. */ class FBatchRenderContext { public: FRenderGraphSetup GraphSetup; FRenderPageBatchDesc BatchDesc; bool bAllowCachedMeshDrawCommands = true; }; /** Mesh processor for rendering static meshes to the virtual texture */ class FRuntimeVirtualTextureMeshProcessor : public FSceneRenderingAllocatorObject, public FMeshPassProcessor { public: FRuntimeVirtualTextureMeshProcessor(const FScene* InScene, ERHIFeatureLevel::Type InFeatureLevel, const FSceneView* InView, FMeshPassDrawListContext* InDrawListContext) : FMeshPassProcessor(EMeshPass::VirtualTexture, InScene, InFeatureLevel, InView, InDrawListContext) { DrawRenderState.SetDepthStencilState(TStaticDepthStencilState::GetRHI()); } private: bool TryAddMeshBatch( const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, int32 StaticMeshId, const FMaterialRenderProxy* MaterialRenderProxy, const FMaterial* Material) { const uint8 OutputAttributeMask = Material->IsDefaultMaterial() ? 0xff : Material->GetRuntimeVirtualTextureOutputAttibuteMask_RenderThread(); if (OutputAttributeMask != 0) { switch ((ERuntimeVirtualTextureMaterialType)MeshBatch.RuntimeVirtualTextureMaterialType) { case ERuntimeVirtualTextureMaterialType::BaseColor: return Process(MeshBatch, BatchElementMask, StaticMeshId, OutputAttributeMask, PrimitiveSceneProxy, *MaterialRenderProxy, *Material); case ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Roughness: return Process(MeshBatch, BatchElementMask, StaticMeshId, OutputAttributeMask, PrimitiveSceneProxy, *MaterialRenderProxy, *Material); case ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Specular: case ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Specular_YCoCg: case ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Specular_Mask_YCoCg: return Process(MeshBatch, BatchElementMask, StaticMeshId, OutputAttributeMask, PrimitiveSceneProxy, *MaterialRenderProxy, *Material); case ERuntimeVirtualTextureMaterialType::Mask4: return Process(MeshBatch, BatchElementMask, StaticMeshId, OutputAttributeMask, PrimitiveSceneProxy, *MaterialRenderProxy, *Material); case ERuntimeVirtualTextureMaterialType::WorldHeight: return Process(MeshBatch, BatchElementMask, StaticMeshId, OutputAttributeMask, PrimitiveSceneProxy, *MaterialRenderProxy, *Material); case ERuntimeVirtualTextureMaterialType::Displacement: return Process(MeshBatch, BatchElementMask, StaticMeshId, OutputAttributeMask, PrimitiveSceneProxy, *MaterialRenderProxy, *Material); default: break; } } return true; } template bool Process( const FMeshBatch& MeshBatch, uint64 BatchElementMask, int32 StaticMeshId, uint8 OutputAttributeMask, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, const FMaterialRenderProxy& RESTRICT MaterialRenderProxy, const FMaterial& RESTRICT MaterialResource) { const FVertexFactory* VertexFactory = MeshBatch.VertexFactory; TMeshProcessorShaders< FShader_VirtualTextureMaterialDraw_VS< MaterialPolicy >, FShader_VirtualTextureMaterialDraw_PS< MaterialPolicy > > VirtualTexturePassShaders; FMaterialShaderTypes ShaderTypes; ShaderTypes.AddShaderType>(); ShaderTypes.AddShaderType>(); FMaterialShaders Shaders; if (!MaterialResource.TryGetShaders(ShaderTypes, VertexFactory->GetType(), Shaders)) { return false; } Shaders.TryGetVertexShader(VirtualTexturePassShaders.VertexShader); Shaders.TryGetPixelShader(VirtualTexturePassShaders.PixelShader); DrawRenderState.SetBlendState(MaterialPolicy::GetBlendState(OutputAttributeMask)); const FMeshDrawingPolicyOverrideSettings OverrideSettings = ComputeMeshOverrideSettings(MeshBatch); ERasterizerFillMode MeshFillMode = ComputeMeshFillMode(MaterialResource, OverrideSettings); ERasterizerCullMode MeshCullMode = ComputeMeshCullMode(MaterialResource, OverrideSettings); FMeshMaterialShaderElementData ShaderElementData; ShaderElementData.InitializeMeshMaterialData(ViewIfDynamicMeshCommand, PrimitiveSceneProxy, MeshBatch, StaticMeshId, false); FMeshDrawCommandSortKey SortKey; SortKey.Translucent.MeshIdInPrimitive = MeshBatch.MeshIdInPrimitive; SortKey.Translucent.Distance = 0; SortKey.Translucent.Priority = (uint16)((int32)PrimitiveSceneProxy->GetTranslucencySortPriority() - (int32)SHRT_MIN); BuildMeshDrawCommands( MeshBatch, BatchElementMask, PrimitiveSceneProxy, MaterialRenderProxy, MaterialResource, DrawRenderState, VirtualTexturePassShaders, MeshFillMode, MeshCullMode, SortKey, EMeshPassFeatures::Default, ShaderElementData); return true; } template void CollectPSOInitializersInternal( const FPSOPrecacheVertexFactoryData& VertexFactoryData, const FMaterial& RESTRICT MaterialResource, const ERasterizerFillMode& MeshFillMode, const ERasterizerCullMode& MeshCullMode, uint8 OutputAttributeMask, ERuntimeVirtualTextureMaterialType MaterialType, TArray& PSOInitializers) { FMaterialShaderTypes ShaderTypes; ShaderTypes.AddShaderType>(); ShaderTypes.AddShaderType>(); FMaterialShaders Shaders; if (!MaterialResource.TryGetShaders(ShaderTypes, VertexFactoryData.VertexFactoryType, Shaders)) { return; } TMeshProcessorShaders< FShader_VirtualTextureMaterialDraw_VS< MaterialPolicy >, FShader_VirtualTextureMaterialDraw_PS< MaterialPolicy > > VirtualTexturePassShaders; Shaders.TryGetVertexShader(VirtualTexturePassShaders.VertexShader); Shaders.TryGetPixelShader(VirtualTexturePassShaders.PixelShader); FMeshPassProcessorRenderState PSODrawRenderState(DrawRenderState); PSODrawRenderState.SetBlendState(MaterialPolicy::GetBlendState(OutputAttributeMask)); const bool bLQQuality = false; FGraphicsPipelineRenderTargetsInfo RenderTargetsInfo; RenderTargetsInfo.NumSamples = 1; FRenderGraphSetup::SetupRenderTargetsInfo(MaterialType, FeatureLevel, bLQQuality, RenderTargetsInfo); AddGraphicsPipelineStateInitializer( VertexFactoryData, MaterialResource, PSODrawRenderState, RenderTargetsInfo, VirtualTexturePassShaders, MeshFillMode, MeshCullMode, PT_TriangleList, EMeshPassFeatures::Default, true /*bRequired*/, PSOInitializers); } public: virtual void AddMeshBatch(const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, int32 StaticMeshId = -1) override final { if (MeshBatch.bRenderToVirtualTexture) { const FMaterialRenderProxy* MaterialRenderProxy = MeshBatch.MaterialRenderProxy; while (MaterialRenderProxy) { const FMaterial* Material = MaterialRenderProxy->GetMaterialNoFallback(FeatureLevel); if (Material && Material->GetRenderingThreadShaderMap()) { if (TryAddMeshBatch(MeshBatch, BatchElementMask, PrimitiveSceneProxy, StaticMeshId, MaterialRenderProxy, Material)) { break; } } MaterialRenderProxy = MaterialRenderProxy->GetFallback(FeatureLevel); } } } virtual void CollectPSOInitializers( const FSceneTexturesConfig& SceneTexturesConfig, const FMaterial& Material, const FPSOPrecacheVertexFactoryData& VertexFactoryData, const FPSOPrecacheParams& PreCacheParams, TArray& PSOInitializers) override final { const uint8 OutputAttributeMask = Material.IsDefaultMaterial() ? 0xff : Material.GetRuntimeVirtualTextureOutputAttibuteMask_GameThread(); if (OutputAttributeMask != 0) { const FMeshDrawingPolicyOverrideSettings OverrideSettings = ComputeMeshOverrideSettings(PreCacheParams); const ERasterizerFillMode MeshFillMode = ComputeMeshFillMode(Material, OverrideSettings); const ERasterizerCullMode MeshCullMode = ComputeMeshCullMode(Material, OverrideSettings); // Tried checking which virtual textures are used on primitive component at PSO level, but if only those types are precached // then quite a few hitches can be seen - if we want to reduce the amount of PSOs to precache here then better investigation // is needed what types should be compiled (currently there are around 300+ PSOs coming from virtual textures after level loading) CollectPSOInitializersInternal(VertexFactoryData, Material, MeshFillMode, MeshCullMode, OutputAttributeMask, ERuntimeVirtualTextureMaterialType::BaseColor, PSOInitializers); CollectPSOInitializersInternal(VertexFactoryData, Material, MeshFillMode, MeshCullMode, OutputAttributeMask, ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Roughness, PSOInitializers); CollectPSOInitializersInternal(VertexFactoryData, Material, MeshFillMode, MeshCullMode, OutputAttributeMask, ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Specular, PSOInitializers); CollectPSOInitializersInternal(VertexFactoryData, Material, MeshFillMode, MeshCullMode, OutputAttributeMask, ERuntimeVirtualTextureMaterialType::BaseColor, PSOInitializers); CollectPSOInitializersInternal(VertexFactoryData, Material, MeshFillMode, MeshCullMode, OutputAttributeMask, ERuntimeVirtualTextureMaterialType::WorldHeight, PSOInitializers); CollectPSOInitializersInternal(VertexFactoryData, Material, MeshFillMode, MeshCullMode, OutputAttributeMask, ERuntimeVirtualTextureMaterialType::Displacement, PSOInitializers); } } private: FMeshPassProcessorRenderState DrawRenderState; }; /** Mesh collector for all dynamic mesh batches */ class FDynamicMeshCollector { public: FDynamicMeshCollector(FRHICommandList& RHICmdList, FSceneRenderingBulkObjectAllocator& Allocator, FScene* Scene, FViewInfo* View) : View(View) , Scene(Scene) , Collector(GMaxRHIFeatureLevel, Allocator) { DynamicVertexBuffer.Init(RHICmdList); DynamicIndexBuffer.Init(RHICmdList); Collector.Start(RHICmdList, DynamicVertexBuffer, DynamicIndexBuffer, RuntimeVirtualReadBuffer); // Create a new primitive collector for this page set only if (Scene->GPUScene.IsEnabled()) { View->DynamicPrimitiveCollector = FGPUScenePrimitiveCollector(Scene->GPUScene.GetCurrentDynamicContext()); } Collector.AddViewMeshArrays( View, &MeshBatchAndRelevances, &SimpleElements, &View->DynamicPrimitiveCollector ); } /** Commit and submit batches */ void Submit(FRDGBuilder& GraphBuilder, FRuntimeVirtualTextureMeshProcessor& Processor, ERuntimeVirtualTextureMaterialType MaterialType) { Collector.Finish(); DynamicVertexBuffer.Commit(); DynamicIndexBuffer.Commit(); if (Scene->GPUScene.IsEnabled()) { View->DynamicPrimitiveCollector.Commit(); } // Avoid uploads on empty submissions if (MeshBatchAndRelevances.IsEmpty()) { return; } Scene->GPUScene.UploadDynamicPrimitiveShaderDataForView(GraphBuilder, *View); // Process all batches marked for virtual texturing for (FMeshBatchAndRelevance& MeshBatch : MeshBatchAndRelevances) { if (MeshBatch.Mesh->bRenderToVirtualTexture && MeshBatch.Mesh->RuntimeVirtualTextureMaterialType == static_cast(MaterialType)) { Processor.AddMeshBatch(*MeshBatch.Mesh, ~0ull, MeshBatch.PrimitiveSceneProxy); } } } void SetPrimitive(FPrimitiveSceneProxy* Proxy, FHitProxyId HitProxyId) { Collector.SetPrimitive(Proxy, HitProxyId); } FMeshElementCollector& GetCollector() { return Collector; } private: FViewInfo* View; FScene* Scene; /** Collection states, simple elements ignored */ FMeshElementCollector Collector; FGlobalDynamicVertexBuffer DynamicVertexBuffer; FGlobalDynamicIndexBuffer DynamicIndexBuffer; FSimpleElementCollector SimpleElements; /** All collected batches */ TArray MeshBatchAndRelevances; }; /** Registration for virtual texture command caching pass */ FMeshPassProcessor* CreateRuntimeVirtualTexturePassProcessor(ERHIFeatureLevel::Type FeatureLevel, const FScene* Scene, const FSceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext) { return new FRuntimeVirtualTextureMeshProcessor(Scene, FeatureLevel, InViewIfDynamicMeshCommand, InDrawListContext); } REGISTER_MESHPASSPROCESSOR_AND_PSOCOLLECTOR(VirtualTexturePass, CreateRuntimeVirtualTexturePassProcessor, EShadingPath::Deferred, EMeshPass::VirtualTexture, EMeshPassFlags::CachedMeshCommands); REGISTER_MESHPASSPROCESSOR_AND_PSOCOLLECTOR(VirtualTexturePassMobile, CreateRuntimeVirtualTexturePassProcessor, EShadingPath::Mobile, EMeshPass::VirtualTexture, EMeshPassFlags::CachedMeshCommands); /** Collect meshes to draw. */ void GatherMeshesToDraw(FDynamicPassMeshDrawListContext* DynamicMeshPassContext, FRDGBuilder& GraphBuilder, FScene* Scene, FViewInfo* View, ERuntimeVirtualTextureMaterialType MaterialType, int32 RuntimeVirtualTextureId, uint8 vLevel, uint8 MaxLevel, bool bAllowCachedMeshDrawCommands) { // Cached draw command collectors const FCachedPassMeshDrawList& SceneDrawList = Scene->CachedDrawLists[EMeshPass::VirtualTexture]; // Uncached mesh processor FRuntimeVirtualTextureMeshProcessor MeshProcessor(Scene, Scene->GetFeatureLevel(), View, DynamicMeshPassContext); // Pre-calculate view factors used for culling const float RcpWorldSize = 1.f / (View->ViewMatrices.GetInvProjectionMatrix().M[0][0]); const float WorldToPixel = View->ViewRect.Width() * RcpWorldSize; TArray PrimitiveIndices; if (FRuntimeVirtualTextureSceneExtension const* SceneExtension = Scene->GetExtensionPtr()) { SceneExtension->GetPrimitivesForRuntimeVirtualTexture(Scene, RuntimeVirtualTextureId, PrimitiveIndices); } // Lazily created on first usage FDynamicMeshCollector* DynamicCollector = nullptr; // Set of views for dynamic collection TArray Views{View}; for (const int32 PrimitiveIndex : PrimitiveIndices) { //todo[vt]: In our case we know that frustum is an oriented box so investigate cheaper test for intersecting that const FSphere SphereBounds = Scene->PrimitiveBounds[PrimitiveIndex].BoxSphereBounds.GetSphere(); if (!View->ViewFrustum.IntersectSphere(SphereBounds.Center, SphereBounds.W)) { continue; } FPrimitiveSceneInfo* RESTRICT PrimitiveSceneInfo = Scene->Primitives[PrimitiveIndex]; // Cull primitives according to mip level or pixel coverage const FPrimitiveRuntimeVirtualTextureLodInfo LodInfo = PrimitiveSceneInfo->GetRuntimeVirtualTextureLodInfo(); if (LodInfo.CullMethod == 0) { if (MaxLevel - vLevel < LodInfo.CullValue) { continue; } } else { // Note that we use 2^MinPixelCoverage as that scales linearly with mip extents int32 PixelCoverage = FMath::FloorToInt(2.f * SphereBounds.W * WorldToPixel); if (PixelCoverage < (1 << LodInfo.CullValue)) { continue; } } FMeshDrawCommandPrimitiveIdInfo IdInfo = PrimitiveSceneInfo->GetMDCIdInfo(); // Calculate Lod for current mip const float AreaRatio = 2.f * SphereBounds.W * RcpWorldSize; const int32 CurFirstLODIdx = PrimitiveSceneInfo->Proxy->GetCurrentFirstLODIdx_RenderThread(); const int32 MinLODIdx = FMath::Max((int32)LodInfo.MinLod, CurFirstLODIdx); const int32 MaxLODIdx = FMath::Max((int32)LodInfo.MaxLod, CurFirstLODIdx); const int32 LodBias = (int32)LodInfo.LodBias - FPrimitiveRuntimeVirtualTextureLodInfo::LodBiasOffset; const int32 LodIndex = FMath::Clamp(LodBias - FMath::FloorToInt(FMath::Log2(AreaRatio)), MinLODIdx, MaxLODIdx); // Process meshes for (int32 MeshIndex = 0; MeshIndex < PrimitiveSceneInfo->StaticMeshes.Num(); ++MeshIndex) { FStaticMeshBatchRelevance const& StaticMeshRelevance = PrimitiveSceneInfo->StaticMeshRelevances[MeshIndex]; if (StaticMeshRelevance.bRenderToVirtualTexture && StaticMeshRelevance.GetLODIndex() == LodIndex && StaticMeshRelevance.RuntimeVirtualTextureMaterialType == (uint32)MaterialType) { bool bCachedDraw = false; if (bAllowCachedMeshDrawCommands && StaticMeshRelevance.bSupportsCachingMeshDrawCommands) { // Use cached draw command const int32 StaticMeshCommandInfoIndex = StaticMeshRelevance.GetStaticMeshCommandInfoIndex(EMeshPass::VirtualTexture); if (StaticMeshCommandInfoIndex >= 0) { FCachedMeshDrawCommandInfo& CachedMeshDrawCommand = PrimitiveSceneInfo->StaticMeshCommandInfos[StaticMeshCommandInfoIndex]; const FMeshDrawCommand* MeshDrawCommand = CachedMeshDrawCommand.StateBucketId >= 0 ? &Scene->CachedMeshDrawCommandStateBuckets[EMeshPass::VirtualTexture].GetByElementId(CachedMeshDrawCommand.StateBucketId).Key : &SceneDrawList.MeshDrawCommands[CachedMeshDrawCommand.CommandIndex]; FVisibleMeshDrawCommand NewVisibleMeshDrawCommand; NewVisibleMeshDrawCommand.Setup( MeshDrawCommand, IdInfo, CachedMeshDrawCommand.StateBucketId, CachedMeshDrawCommand.MeshFillMode, CachedMeshDrawCommand.MeshCullMode, CachedMeshDrawCommand.Flags, CachedMeshDrawCommand.SortKey, CachedMeshDrawCommand.CullingPayload, EMeshDrawCommandCullingPayloadFlags::NoScreenSizeCull); DynamicMeshPassContext->AddVisibleMeshDrawCommand(NewVisibleMeshDrawCommand); bCachedDraw = true; } } if (!bCachedDraw) { // No cached draw command was available. Process the mesh batch. uint64 BatchElementMask = ~0ull; MeshProcessor.AddMeshBatch(PrimitiveSceneInfo->StaticMeshes[MeshIndex], BatchElementMask, Scene->PrimitiveSceneProxies[PrimitiveIndex]); } } } // It's not entirely accurate to equate the dynamic view relevance here, so, assume dynamic // mesh batches if no static were supplied. For now at least. if (PrimitiveSceneInfo->StaticMeshes.IsEmpty()) { // Lazy create if (!DynamicCollector) { DynamicCollector = View->Allocator.Create(GraphBuilder.RHICmdList, View->Allocator, Scene, View); } // Collect all dynamic batches DynamicCollector->SetPrimitive(PrimitiveSceneInfo->Proxy, PrimitiveSceneInfo->DefaultDynamicHitProxyId); PrimitiveSceneInfo->Proxy->GetDynamicMeshElements(Views, *View->Family, 0x1, DynamicCollector->GetCollector()); } } // Process collected batches if (DynamicCollector) { DynamicCollector->Submit(GraphBuilder, MeshProcessor, MaterialType); } } /** BC Compression compute shader */ class FShader_VirtualTextureCompress : public FGlobalShader { public: BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER(FUintVector4, SourceRect) SHADER_PARAMETER_SCALAR_ARRAY(int32, DestPos, [MaxRenderPageBatch * MaxTextureLayers * 2]) SHADER_PARAMETER_STRUCT_REF(FEtcParameters, EtcParameters) SHADER_PARAMETER_STRUCT_REF(FAstcParameters, AstcParameters) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, RenderTexture0) SHADER_PARAMETER_SAMPLER(SamplerState, TextureSampler0) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, RenderTexture1) SHADER_PARAMETER_SAMPLER(SamplerState, TextureSampler1) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, RenderTexture2) SHADER_PARAMETER_SAMPLER(SamplerState, TextureSampler2) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, OutCompressTexture0_64bit) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, OutCompressTexture1_64bit) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, OutCompressTexture2_64bit) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, OutCompressTexture0_128bit) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, OutCompressTexture1_128bit) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, OutCompressTexture2_128bit) END_SHADER_PARAMETER_STRUCT() class FUseSrcTextureArray : SHADER_PERMUTATION_BOOL("USE_SRC_TEXTURE_ARRAY"); class FUseDstTextureArray : SHADER_PERMUTATION_BOOL("USE_DST_TEXTURE_ARRAY"); class FAstcHighProfile : SHADER_PERMUTATION_BOOL("ASTC_HIGH_PROFILE"); using FPermutationDomain = TShaderPermutationDomain; static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("ETC_PROFILE"), UseEtcProfile(Parameters.Platform) ? 1 : 0); OutEnvironment.SetDefine(TEXT("ASTC_PROFILE"), UseAstcProfile(Parameters.Platform) ? 1 : 0); OutEnvironment.SetDefine(TEXT("PACK_RG32_RGBA16"), UseRGBA16(Parameters.Platform) ? 1 : 0); OutEnvironment.SetDefine(TEXT("MAX_BATCH_SIZE"), MaxRenderPageBatch); OutEnvironment.SetDefine(TEXT("MAX_DST_LAYERS"), MaxTextureLayers); const FPermutationDomain PermutationVector(Parameters.PermutationId); const bool bUseSrcTextureArray = PermutationVector.Get(); OutEnvironment.SetDefine(TEXT("BLOCK_COMPRESS_SRC_TEXTURE_ARRAY"), bUseSrcTextureArray ? 1 : 0); } static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { FPermutationDomain PermutationVector(Parameters.PermutationId); if (!PermutationVector.Get() && PermutationVector.Get()) { // No compress pass goes from simple source texture to destination array texture. return false; } if (PermutationVector.Get()) { return UseAstcHighProfile(Parameters.Platform); } return true; } FShader_VirtualTextureCompress() {} FShader_VirtualTextureCompress(const ShaderMetaType::CompiledShaderInitializerType& Initializer) : FGlobalShader(Initializer) { Bindings.BindForLegacyShaderParameters(this, Initializer.PermutationId, Initializer.ParameterMap, *FParameters::FTypeInfo::GetStructMetadata()); } }; template< ERuntimeVirtualTextureMaterialType MaterialType > class FShader_VirtualTextureCompress_CS : public FShader_VirtualTextureCompress { public: typedef FShader_VirtualTextureCompress_CS< MaterialType > ClassName; // typedef is only so that we can use in DECLARE_SHADER_TYPE macro DECLARE_SHADER_TYPE( ClassName, Global ); FShader_VirtualTextureCompress_CS() {} FShader_VirtualTextureCompress_CS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) : FShader_VirtualTextureCompress(Initializer) {} static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return UseVirtualTexturing(Parameters.Platform) && RuntimeVirtualTexture::IsMaterialTypeSupported(MaterialType, Parameters.Platform); } }; IMPLEMENT_SHADER_TYPE(template<>, FShader_VirtualTextureCompress_CS< ERuntimeVirtualTextureMaterialType::BaseColor >, TEXT("/Engine/Private/VirtualTextureCompress.usf"), TEXT("CompressBaseColorCS"), SF_Compute); IMPLEMENT_SHADER_TYPE(template<>, FShader_VirtualTextureCompress_CS< ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Specular >, TEXT("/Engine/Private/VirtualTextureCompress.usf"), TEXT("CompressBaseColorNormalSpecularCS"), SF_Compute); IMPLEMENT_SHADER_TYPE(template<>, FShader_VirtualTextureCompress_CS< ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Roughness >, TEXT("/Engine/Private/VirtualTextureCompress.usf"), TEXT("CompressBaseColorNormalRoughnessCS"), SF_Compute); IMPLEMENT_SHADER_TYPE(template<>, FShader_VirtualTextureCompress_CS< ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Specular_YCoCg >, TEXT("/Engine/Private/VirtualTextureCompress.usf"), TEXT("CompressBaseColorNormalSpecularYCoCgCS"), SF_Compute); IMPLEMENT_SHADER_TYPE(template<>, FShader_VirtualTextureCompress_CS< ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Specular_Mask_YCoCg >, TEXT("/Engine/Private/VirtualTextureCompress.usf"), TEXT("CompressBaseColorNormalSpecularMaskYCoCgCS"), SF_Compute); IMPLEMENT_SHADER_TYPE(template<>, FShader_VirtualTextureCompress_CS< ERuntimeVirtualTextureMaterialType::Mask4 >, TEXT("/Engine/Private/VirtualTextureCompress.usf"), TEXT("CompressMask4CS"), SF_Compute); IMPLEMENT_SHADER_TYPE(template<>, FShader_VirtualTextureCompress_CS< ERuntimeVirtualTextureMaterialType::Displacement >, TEXT("/Engine/Private/VirtualTextureCompress.usf"), TEXT("CompressDisplacementCS"), SF_Compute); /** Add the BC compression pass to the graph. */ template< ERuntimeVirtualTextureMaterialType MaterialType > void AddCompressPass(FRDGBuilder& GraphBuilder, ERHIFeatureLevel::Type FeatureLevel, FShader_VirtualTextureCompress::FParameters* Parameters, FIntVector GroupCount, bool bDirectAliasing) { FGlobalShaderMap* GlobalShaderMap = GetGlobalShaderMap(FeatureLevel); FShader_VirtualTextureCompress::FPermutationDomain PermutationVector; PermutationVector.Set(GroupCount.Z > 1); PermutationVector.Set(GroupCount.Z > 1 && !bDirectAliasing); PermutationVector.Set(UseAstcHighProfile(GShaderPlatformForFeatureLevel[FeatureLevel])); TShaderMapRef< FShader_VirtualTextureCompress_CS< MaterialType > > ComputeShader(GlobalShaderMap, PermutationVector); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("VirtualTextureCompress"), ComputeShader, Parameters, GroupCount); } /** Set up the BC compression pass for the given MaterialType. */ void AddCompressPass( FRDGBuilder& GraphBuilder, ERHIFeatureLevel::Type FeatureLevel, FShader_VirtualTextureCompress::FParameters* Parameters, FIntPoint TextureSize, int32 NumSlices, ERuntimeVirtualTextureMaterialType MaterialType, bool bDirectAliasing) { const FIntVector GroupCount(((TextureSize.X / 4) + 7) / 8, ((TextureSize.Y / 4) + 7) / 8, NumSlices); // Dispatch using the shader variation for our MaterialType switch (MaterialType) { case ERuntimeVirtualTextureMaterialType::BaseColor: AddCompressPass(GraphBuilder, FeatureLevel, Parameters, GroupCount, bDirectAliasing); break; case ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Specular: AddCompressPass(GraphBuilder, FeatureLevel, Parameters, GroupCount, bDirectAliasing); break; case ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Roughness: AddCompressPass(GraphBuilder, FeatureLevel, Parameters, GroupCount, bDirectAliasing); break; case ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Specular_YCoCg: AddCompressPass(GraphBuilder, FeatureLevel, Parameters, GroupCount, bDirectAliasing); break; case ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Specular_Mask_YCoCg: AddCompressPass(GraphBuilder, FeatureLevel, Parameters, GroupCount, bDirectAliasing); break; case ERuntimeVirtualTextureMaterialType::Mask4: AddCompressPass(GraphBuilder, FeatureLevel, Parameters, GroupCount, bDirectAliasing); break; case ERuntimeVirtualTextureMaterialType::Displacement: AddCompressPass(GraphBuilder, FeatureLevel, Parameters, GroupCount, bDirectAliasing); break; } } /** Copy shaders are used when compression is disabled. These are used to ensure that the channel layout is the same as with compression. */ class FShader_VirtualTextureCopy : public FGlobalShader { public: BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) RENDER_TARGET_BINDING_SLOTS() SHADER_PARAMETER(FIntVector4, DestRect) SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, RenderTexture0) SHADER_PARAMETER_SAMPLER(SamplerState, TextureSampler0) SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, RenderTexture1) SHADER_PARAMETER_SAMPLER(SamplerState, TextureSampler1) SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, RenderTexture2) SHADER_PARAMETER_SAMPLER(SamplerState, TextureSampler2) END_SHADER_PARAMETER_STRUCT() FShader_VirtualTextureCopy() {} FShader_VirtualTextureCopy(const ShaderMetaType::CompiledShaderInitializerType& Initializer) : FGlobalShader(Initializer) { Bindings.BindForLegacyShaderParameters(this, Initializer.PermutationId, Initializer.ParameterMap, *FParameters::FTypeInfo::GetStructMetadata()); } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("MAX_BATCH_SIZE"), 1); OutEnvironment.SetDefine(TEXT("MAX_DST_LAYERS"), 1); } }; class FShader_VirtualTextureCopy_VS : public FShader_VirtualTextureCopy { public: DECLARE_SHADER_TYPE(FShader_VirtualTextureCopy_VS, Global); FShader_VirtualTextureCopy_VS() {} FShader_VirtualTextureCopy_VS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) : FShader_VirtualTextureCopy(Initializer) {} }; IMPLEMENT_SHADER_TYPE(, FShader_VirtualTextureCopy_VS, TEXT("/Engine/Private/VirtualTextureCompress.usf"), TEXT("CopyVS"), SF_Vertex); template< ERuntimeVirtualTextureMaterialType MaterialType > class FShader_VirtualTextureCopy_PS : public FShader_VirtualTextureCopy { public: typedef FShader_VirtualTextureCopy_PS< MaterialType > ClassName; // typedef is only so that we can use in DECLARE_SHADER_TYPE macro DECLARE_SHADER_TYPE(ClassName, Global); FShader_VirtualTextureCopy_PS() {} FShader_VirtualTextureCopy_PS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) : FShader_VirtualTextureCopy(Initializer) {} }; IMPLEMENT_SHADER_TYPE(template<>, FShader_VirtualTextureCopy_PS< ERuntimeVirtualTextureMaterialType::BaseColor >, TEXT("/Engine/Private/VirtualTextureCompress.usf"), TEXT("CopyBaseColorPS"), SF_Pixel); IMPLEMENT_SHADER_TYPE(template<>, FShader_VirtualTextureCopy_PS< ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Specular >, TEXT("/Engine/Private/VirtualTextureCompress.usf"), TEXT("CopyBaseColorNormalSpecularPS"), SF_Pixel); IMPLEMENT_SHADER_TYPE(template<>, FShader_VirtualTextureCopy_PS< ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Specular_YCoCg >, TEXT("/Engine/Private/VirtualTextureCompress.usf"), TEXT("CopyBaseColorNormalSpecularYCoCgPS"), SF_Pixel); IMPLEMENT_SHADER_TYPE(template<>, FShader_VirtualTextureCopy_PS< ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Specular_Mask_YCoCg >, TEXT("/Engine/Private/VirtualTextureCompress.usf"), TEXT("CopyBaseColorNormalSpecularMaskYCoCgPS"), SF_Pixel); IMPLEMENT_SHADER_TYPE(template<>, FShader_VirtualTextureCopy_PS< ERuntimeVirtualTextureMaterialType::Mask4 >, TEXT("/Engine/Private/VirtualTextureCompress.usf"), TEXT("CopyMask4PS"), SF_Pixel); IMPLEMENT_SHADER_TYPE(template<>, FShader_VirtualTextureCopy_PS< ERuntimeVirtualTextureMaterialType::WorldHeight >, TEXT("/Engine/Private/VirtualTextureCompress.usf"), TEXT("CopyWorldHeightPS"), SF_Pixel); /** Add the copy pass to the graph. */ template< ERuntimeVirtualTextureMaterialType MaterialType > void AddCopyPass(FRDGBuilder& GraphBuilder, ERHIFeatureLevel::Type FeatureLevel, FShader_VirtualTextureCopy::FParameters* Parameters, FIntPoint TextureSize) { FGlobalShaderMap* GlobalShaderMap = GetGlobalShaderMap(FeatureLevel); TShaderMapRef< FShader_VirtualTextureCopy_VS > VertexShader(GlobalShaderMap); TShaderMapRef< FShader_VirtualTextureCopy_PS< MaterialType > > PixelShader(GlobalShaderMap); GraphBuilder.AddPass( RDG_EVENT_NAME("VirtualTextureCopy"), Parameters, ERDGPassFlags::Raster, [VertexShader, PixelShader, Parameters, TextureSize](FRDGAsyncTask, FRHICommandList& RHICmdList) { FGraphicsPipelineStateInitializer GraphicsPSOInit; RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit); GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState::GetRHI(); GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI(); GraphicsPSOInit.PrimitiveType = PT_TriangleList; GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GetVertexDeclarationFVector4(); GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader(); GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader(); SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0); SetShaderParameters(RHICmdList, VertexShader, VertexShader.GetVertexShader(), *Parameters); SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), *Parameters); RHICmdList.SetViewport(0.0f, 0.0f, 0.0f, TextureSize[0], TextureSize[1], 1.0f); RHICmdList.DrawIndexedPrimitive(GTwoTrianglesIndexBuffer.IndexBufferRHI, 0, 0, 4, 0, 2, 1); }); } /** Set up the copy pass for the given MaterialType. */ void AddCopyPass(FRDGBuilder& GraphBuilder, ERHIFeatureLevel::Type FeatureLevel, FShader_VirtualTextureCopy::FParameters* Parameters, FIntPoint TextureSize, ERuntimeVirtualTextureMaterialType MaterialType) { switch (MaterialType) { case ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Specular: AddCopyPass(GraphBuilder, FeatureLevel, Parameters, TextureSize); break; case ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Specular_YCoCg: AddCopyPass(GraphBuilder, FeatureLevel, Parameters, TextureSize); break; case ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Specular_Mask_YCoCg: AddCopyPass(GraphBuilder, FeatureLevel, Parameters, TextureSize); break; case ERuntimeVirtualTextureMaterialType::Mask4: AddCopyPass(GraphBuilder, FeatureLevel, Parameters, TextureSize); break; } } /** Set up the copy pass for the given MaterialType. */ void AddCopyThumbnailPass(FRDGBuilder& GraphBuilder, ERHIFeatureLevel::Type FeatureLevel, FShader_VirtualTextureCopy::FParameters* Parameters, FIntPoint TextureSize, ERuntimeVirtualTextureMaterialType MaterialType) { switch (MaterialType) { case ERuntimeVirtualTextureMaterialType::BaseColor: case ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Roughness: case ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Specular: case ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Specular_YCoCg: case ERuntimeVirtualTextureMaterialType::BaseColor_Normal_Specular_Mask_YCoCg: AddCopyPass(GraphBuilder, FeatureLevel, Parameters, TextureSize); break; case ERuntimeVirtualTextureMaterialType::Mask4: AddCopyPass(GraphBuilder, FeatureLevel, Parameters, TextureSize); break; case ERuntimeVirtualTextureMaterialType::WorldHeight: case ERuntimeVirtualTextureMaterialType::Displacement: AddCopyPass(GraphBuilder, FeatureLevel, Parameters, TextureSize); break; } } /** Mesh render pass prologue to set the viewport. Also applies a page corruption workaround when that is enabled. */ void MeshPassPrologue(FRHICommandList& RHICmdList, FIntRect const& InViewRect, int32 InPageIndex, EShaderPlatform InShaderPlatform) { if (InPageIndex == 0 && CVarVTApplyPageCorruptionFix.GetValueOnRenderThread() && IsPCPlatform(InShaderPlatform) && IsD3DPlatform(InShaderPlatform)) { // Workaround fix for an issue where runtime virtual texture page corruption causes square artifacts. // Repro of the bug is rare. But it's been found that inserting a single call to ID3D12GraphicsCommandList::SetPipelineState() // before rendering any RVT pages resolves the issue. FGlobalShaderMap* GlobalShaderMap = GetGlobalShaderMap(GMaxRHIFeatureLevel); TShaderMapRef VertexShader(GlobalShaderMap); TShaderMapRef> PixelShader(GlobalShaderMap); FGraphicsPipelineStateInitializer GraphicsPSOInit; GraphicsPSOInit.RenderTargetsEnabled = 1; GraphicsPSOInit.RenderTargetFormats[0] = PF_B8G8R8A8; GraphicsPSOInit.RenderTargetFlags[0] = TexCreate_None; GraphicsPSOInit.NumSamples = 1; GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState::GetRHI(); GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI(); GraphicsPSOInit.PrimitiveType = PT_TriangleList; GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GetVertexDeclarationFVector4(); GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader(); GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader(); SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0, EApplyRendertargetOption::DoNothing); } RHICmdList.SetViewport(static_cast(InViewRect.Min.X), static_cast(InViewRect.Min.Y), 0.0f, static_cast(InViewRect.Max.X), static_cast(InViewRect.Max.Y), 1.0f); } /** Get the debug color to use for a given mip level. Returns InDefaultColor if mip debugging is disabled. */ FLinearColor GetDebugMipLevelColor(uint32 InLevel, FLinearColor InDefaultColor) { const int32 MipColorMode = CVarVTMipColors.GetValueOnRenderThread(); if (MipColorMode == 1) { static const uint32 MipColors[] = { 0xC0FFFFFF, 0xC0FFFF00, 0xC000FFFF, 0xC000FF00, 0xC0FF00FF, 0xC0FF0000, 0xC00000FF, 0xC0808080, 0xC0808000, 0xC0008080, 0xC0008000, 0xC0800080, 0xC0800000, 0xC0000080 }; InLevel = FMath::Min(InLevel, sizeof(MipColors) / sizeof(MipColors[0]) - 1); return FLinearColor(FColor(MipColors[InLevel])); } else if (MipColorMode == 2 && InLevel == 0) { return FLinearColor(1.f, 0.f, 1.f, 0.5f); } return InDefaultColor; } /** * Render a single page from a batch. * todo[vt]: Can we add some batch rendering mesh pass where all prerequesite BuildRenderingCommands/Compute phases are batched and then all Graphics draws are batched. */ void RenderPage(FRDGBuilder& GraphBuilder, FBatchRenderContext const& BatchRenderContext, int32 PageIndex) { CSV_CUSTOM_STAT(VirtualTexturing, RenderedPages, 1, ECsvCustomStatOp::Accumulate); INC_DWORD_STAT_BY(STAT_RenderedPages, 1); FRenderGraphSetup const& GraphSetup = BatchRenderContext.GraphSetup; FRenderPageBatchDesc const& BatchDesc = BatchRenderContext.BatchDesc; FRenderPageDesc const& PageDesc = BatchDesc.PageDescs[PageIndex]; FScene* Scene = BatchDesc.SceneRenderer->GetScene(); // Initialize the view required for the material render pass FSceneViewFamily::ConstructionValues ViewFamilyInit(nullptr, Scene, FEngineShowFlags(ESFIM_Game)); ViewFamilyInit.SetTime(FGameTime()); FViewFamilyInfo& ViewFamily = *GraphBuilder.AllocObject(ViewFamilyInit); ViewFamily.SetSceneRenderer(BatchDesc.SceneRenderer); FSceneViewInitOptions ViewInitOptions; ViewInitOptions.ViewFamily = &ViewFamily; const FIntPoint TextureSize = PageDesc.DestRect[0].Size(); ViewInitOptions.SetViewRectangle(FIntRect(FIntPoint(0, 0), TextureSize)); FBox2D const& UVRange = PageDesc.UVRange; const FVector UVCenter = FVector(UVRange.GetCenter(), 0.f); FTransform const& UVToWorld = BatchDesc.UVToWorld; const FVector CameraLookAt = UVToWorld.TransformPosition(UVCenter); const float BoundBoxZ = UVToWorld.GetScale3D().Z; const FVector CameraPos = CameraLookAt + BoundBoxZ * UVToWorld.GetUnitAxis(EAxis::Z); ViewInitOptions.ViewOrigin = CameraPos; const float OrthoWidth = UVToWorld.GetScaledAxis(EAxis::X).Size() * UVRange.GetExtent().X; const float OrthoHeight = UVToWorld.GetScaledAxis(EAxis::Y).Size() * UVRange.GetExtent().Y; const FTransform WorldToUVRotate(UVToWorld.GetRotation().Inverse()); ViewInitOptions.ViewRotationMatrix = WorldToUVRotate.ToMatrixNoScale() * FMatrix( FPlane(1, 0, 0, 0), FPlane(0, -1, 0, 0), FPlane(0, 0, -1, 0), FPlane(0, 0, 0, 1)); const float NearPlane = 0; const float FarPlane = BoundBoxZ; const float ZScale = 1.0f / (FarPlane - NearPlane); const float ZOffset = -NearPlane; ViewInitOptions.ProjectionMatrix = FReversedZOrthoMatrix(OrthoWidth, OrthoHeight, ZScale, ZOffset); const uint8 vLevel = PageDesc.vLevel; const uint8 MaxLevel = BatchDesc.MaxLevel; const FVector4f MipLevelParameter = FVector4f((float)vLevel, (float)MaxLevel, OrthoWidth / (float)TextureSize.X, OrthoHeight / (float)TextureSize.Y); FBox const& WorldBounds = BatchDesc.WorldBounds; const float HeightRange = FMath::Max(WorldBounds.Max.Z - WorldBounds.Min.Z, 1.f); const FVector2D WorldHeightPackParameter = FVector2D(1.f / HeightRange, -WorldBounds.Min.Z / HeightRange); ViewInitOptions.BackgroundColor = FLinearColor::Black; ViewInitOptions.OverlayColor = FLinearColor::White; FViewInfo* View = GraphBuilder.AllocObject(ViewInitOptions); ViewFamily.Views.Add(View); View->bIsVirtualTexture = true; View->ViewRect = View->UnconstrainedViewRect; View->CachedViewUniformShaderParameters = MakeUnique(); View->SetupUniformBufferParameters(nullptr, 0, *View->CachedViewUniformShaderParameters); View->ViewUniformBuffer = TUniformBufferRef::CreateUniformBufferImmediate(*View->CachedViewUniformShaderParameters, UniformBuffer_SingleFrame); { RenderCaptureInterface::FScopedCapture RenderCapture((RenderCaptureNextRVTPagesDraws != 0), GraphBuilder, TEXT("RenderRVTPage")); RenderCaptureNextRVTPagesDraws = FMath::Max(RenderCaptureNextRVTPagesDraws - 1, 0); ERenderTargetLoadAction LoadAction = BatchDesc.bClearTextures ? ERenderTargetLoadAction::EClear : ERenderTargetLoadAction::ENoAction; FShader_VirtualTextureMaterialDraw::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->View = View->ViewUniformBuffer; PassParameters->Scene = BatchDesc.SceneRenderer->GetSceneUniformBufferRef(GraphBuilder); FRuntimeVirtualTexturePassParameters* RuntimeVirtualTexturePassParameters = GraphBuilder.AllocParameters(); RuntimeVirtualTexturePassParameters->MipLevel = MipLevelParameter; RuntimeVirtualTexturePassParameters->CustomMaterialData = BatchDesc.CustomMaterialData; RuntimeVirtualTexturePassParameters->DebugParams = GetDebugMipLevelColor(vLevel, BatchDesc.FixedColor); RuntimeVirtualTexturePassParameters->PackHeight = FVector2f(WorldHeightPackParameter); // LWC_TODO: Precision loss PassParameters->RuntimeVirtualTexturePassParameters = GraphBuilder.CreateUniformBuffer(RuntimeVirtualTexturePassParameters); PassParameters->RenderTargets[0] = GraphSetup.RenderTexture0 ? FRenderTargetBinding(GraphSetup.RenderTexture0, LoadAction, 0, PageIndex) : FRenderTargetBinding(); PassParameters->RenderTargets[1] = GraphSetup.RenderTexture1 ? FRenderTargetBinding(GraphSetup.RenderTexture1, LoadAction, 0, PageIndex) : FRenderTargetBinding(); PassParameters->RenderTargets[2] = GraphSetup.RenderTexture2 ? FRenderTargetBinding(GraphSetup.RenderTexture2, LoadAction, 0, PageIndex) : FRenderTargetBinding(); const int32 RuntimeVirtualTextureId = BatchDesc.RuntimeVirtualTextureId; const ERuntimeVirtualTextureMaterialType MaterialType = BatchDesc.MaterialType; const bool bAllowCachedMeshDrawCommands = BatchRenderContext.bAllowCachedMeshDrawCommands; AddSimpleMeshPass(GraphBuilder, PassParameters, Scene, *View, nullptr, RDG_EVENT_NAME("VirtualTextureDraw"), ERDGPassFlags::Raster | ERDGPassFlags::NeverMerge, [Scene, &GraphBuilder, View, MaterialType, RuntimeVirtualTextureId, vLevel, MaxLevel, bAllowCachedMeshDrawCommands](FDynamicPassMeshDrawListContext* DynamicMeshPassContext) { GatherMeshesToDraw(DynamicMeshPassContext, GraphBuilder, Scene, View, MaterialType, RuntimeVirtualTextureId, vLevel, MaxLevel, bAllowCachedMeshDrawCommands); }, [ViewRect = View->ViewRect, PageIndex, ShaderPlatform = Scene->GetShaderPlatform()](FRHICommandList& RHICmdList) { MeshPassPrologue(RHICmdList, ViewRect, PageIndex, ShaderPlatform); }); } } /** * Copy a single rendered page doing any attribute packing. * This path is rarely used, since most use cases want to compress the results of rendering. * We use a pixel shader, but could use a compute shader which batches multiple pages. */ void CopyPage(FRDGBuilder& GraphBuilder, FBatchRenderContext const& BatchRenderContext, int32 PageIndex) { FRenderGraphSetup const& GraphSetup = BatchRenderContext.GraphSetup; FRenderPageBatchDesc const& BatchDesc = BatchRenderContext.BatchDesc; FRenderPageDesc const& PageDesc = BatchDesc.PageDescs[PageIndex]; const int32 ArraySlice = BatchDesc.NumPageDescs > 1 ? PageIndex : -1; FScene* Scene = BatchDesc.SceneRenderer->GetScene(); const FIntPoint TextureSize = PageDesc.DestRect[0].Size(); FShader_VirtualTextureCopy::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->RenderTargets[0] = GraphSetup.CopyTexture0 ? FRenderTargetBinding(GraphSetup.CopyTexture0, ERenderTargetLoadAction::ENoAction, 0, PageIndex) : FRenderTargetBinding(); PassParameters->RenderTargets[1] = GraphSetup.CopyTexture1 ? FRenderTargetBinding(GraphSetup.CopyTexture1, ERenderTargetLoadAction::ENoAction, 0, PageIndex) : FRenderTargetBinding(); PassParameters->RenderTargets[2] = GraphSetup.CopyTexture2 ? FRenderTargetBinding(GraphSetup.CopyTexture2, ERenderTargetLoadAction::ENoAction, 0, PageIndex) : FRenderTargetBinding(); PassParameters->DestRect = FIntVector4(0, 0, TextureSize.X, TextureSize.Y); PassParameters->RenderTexture0 = FRenderGraphSetup::CreateTextureSRV(GraphBuilder, GraphSetup.RenderTexture0, ArraySlice); PassParameters->TextureSampler0 = TStaticSamplerState::GetRHI(); PassParameters->RenderTexture1 = FRenderGraphSetup::CreateTextureSRV(GraphBuilder, GraphSetup.RenderTexture1, ArraySlice); PassParameters->TextureSampler1 = TStaticSamplerState::GetRHI(); PassParameters->RenderTexture2 = FRenderGraphSetup::CreateTextureSRV(GraphBuilder, GraphSetup.RenderTexture2, ArraySlice); PassParameters->TextureSampler2 = TStaticSamplerState::GetRHI(); if (GraphSetup.bCopyPass) { AddCopyPass(GraphBuilder, Scene->GetFeatureLevel(), PassParameters, TextureSize, BatchDesc.MaterialType); } else { AddCopyThumbnailPass(GraphBuilder, Scene->GetFeatureLevel(), PassParameters, TextureSize, BatchDesc.MaterialType); } } /** Compress all pages in a batch. */ void CompressPages(FRDGBuilder& GraphBuilder, FBatchRenderContext const& BatchRenderContext) { FRenderGraphSetup const& GraphSetup = BatchRenderContext.GraphSetup; FRenderPageBatchDesc const& BatchDesc = BatchRenderContext.BatchDesc; FScene* Scene = BatchDesc.SceneRenderer->GetScene(); const FIntPoint TextureSize = BatchDesc.PageDescs[0].DestRect[0].Size(); FShader_VirtualTextureCompress::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->SourceRect = FUintVector4(0, 0, TextureSize.X, TextureSize.Y); PassParameters->EtcParameters = GetEtcParametersUniformBufferRef(); PassParameters->AstcParameters = GetAstcParametersUniformBufferRef(); PassParameters->RenderTexture0 = GraphSetup.RenderTexture0; PassParameters->TextureSampler0 = TStaticSamplerState::GetRHI(); PassParameters->RenderTexture1 = GraphSetup.RenderTexture1; PassParameters->TextureSampler1 = TStaticSamplerState::GetRHI(); PassParameters->RenderTexture2 = GraphSetup.RenderTexture2; PassParameters->TextureSampler2 = TStaticSamplerState::GetRHI(); PassParameters->OutCompressTexture0_64bit = GraphSetup.CompressTextureUAV0_64bit; PassParameters->OutCompressTexture1_64bit = GraphSetup.CompressTextureUAV1_64bit; PassParameters->OutCompressTexture2_64bit = GraphSetup.CompressTextureUAV2_64bit; PassParameters->OutCompressTexture0_128bit = GraphSetup.CompressTextureUAV0_128bit; PassParameters->OutCompressTexture1_128bit = GraphSetup.CompressTextureUAV1_128bit; PassParameters->OutCompressTexture2_128bit = GraphSetup.CompressTextureUAV2_128bit; for (int32 PageIndex = 0; PageIndex < BatchDesc.NumPageDescs; ++PageIndex) { FRenderPageDesc const& PageDesc = BatchDesc.PageDescs[PageIndex]; for (int32 LayerIndex = 0; LayerIndex < MaxTextureLayers; ++LayerIndex) { const int32 WriteIndex = (PageIndex * MaxTextureLayers + LayerIndex) * 2; // Direct aliasing case assumes needs to adjust dest position for BC block size. const int32 DestPosX = GraphSetup.bDirectAliasing ? PageDesc.DestRect[LayerIndex].Min.X / 4 : 0; const int32 DestPosY = GraphSetup.bDirectAliasing ? PageDesc.DestRect[LayerIndex].Min.Y / 4 : 0; GET_SCALAR_ARRAY_ELEMENT(PassParameters->DestPos, WriteIndex) = DestPosX; GET_SCALAR_ARRAY_ELEMENT(PassParameters->DestPos, WriteIndex + 1) = DestPosY; } } AddCompressPass(GraphBuilder, Scene->GetFeatureLevel(), PassParameters, TextureSize, BatchDesc.NumPageDescs, BatchDesc.MaterialType, GraphSetup.bDirectAliasing); } /** Copy all pages in a batch to the final output textures. */ void CopyPagesToOutput(FRDGBuilder& GraphBuilder, FBatchRenderContext const& BatchRenderContext) { FRenderGraphSetup const& GraphSetup = BatchRenderContext.GraphSetup; if (GraphSetup.OutputAlias0 == nullptr && GraphSetup.OutputAlias1 == nullptr && GraphSetup.OutputAlias2 == nullptr) { return; } FRenderPageBatchDesc const& BatchDesc = BatchRenderContext.BatchDesc; const FRDGTextureRef SourceTexture[MaxTextureLayers] = { GraphSetup.OutputAlias0, GraphSetup.OutputAlias1, GraphSetup.OutputAlias2 }; const FRDGTextureRef DestTexture[MaxTextureLayers] = { GraphSetup.TargetTexture0, GraphSetup.TargetTexture1, GraphSetup.TargetTexture2 }; const FIntVector CopySize = SourceTexture[0] ? SourceTexture[0]->Desc.GetSize() : FIntVector(0, 0, 0); for (int32 PageIndex = 0; PageIndex < BatchDesc.NumPageDescs; ++PageIndex) { FRenderPageDesc const& PageDesc = BatchDesc.PageDescs[PageIndex]; for (int32 LayerIndex = 0; LayerIndex < MaxTextureLayers; ++LayerIndex) { if (SourceTexture[LayerIndex] != nullptr && DestTexture[LayerIndex] != nullptr) { FRHICopyTextureInfo CopyInfo; CopyInfo.Size = CopySize; CopyInfo.SourceSliceIndex = PageIndex; CopyInfo.DestPosition = FIntVector(PageDesc.DestRect[LayerIndex].Min.X, PageDesc.DestRect[LayerIndex].Min.Y, 0); AddCopyTexturePass(GraphBuilder, SourceTexture[LayerIndex], DestTexture[LayerIndex], CopyInfo); } } } } bool IsSceneReadyToRender(FSceneInterface* Scene) { return Scene != nullptr && Scene->GetRenderScene() != nullptr && Scene->GetRenderScene()->GPUScene.IsRendering(); } FBatchRenderContext const* InitPageBatch(FRDGBuilder& GraphBuilder, FRenderPageBatchDesc const& InDesc) { FBatchRenderContext* Context = GraphBuilder.AllocObject(); Context->GraphSetup.Init(GraphBuilder, FRenderGraphSetup::FInitDesc(InDesc)); Context->BatchDesc = InDesc; return Context; } void RenderPageBatch(FRDGBuilder& GraphBuilder, FBatchRenderContext const& InContext) { FRenderGraphSetup const& GraphSetup = InContext.GraphSetup; FRenderPageBatchDesc const& Desc = InContext.BatchDesc; if (GraphSetup.bRenderPass) { for (int32 PageIndex = 0; PageIndex < Desc.NumPageDescs; ++PageIndex) { RenderPage(GraphBuilder, InContext, PageIndex); } } if (GraphSetup.bCopyPass || GraphSetup.bCopyThumbnailPass) { for (int32 PageIndex = 0; PageIndex < Desc.NumPageDescs; ++PageIndex) { CopyPage(GraphBuilder, InContext, PageIndex); } } // Batch compress pages now if not direct aliasing the final output texture. // This can reduce the memory high water mark. // If we are direct aliasing then we must defer compression to FinalizePageBatch(). if (GraphSetup.bCompressPass && !InContext.GraphSetup.bDirectAliasing) { CompressPages(GraphBuilder, InContext); } } void FinalizePageBatch(FRDGBuilder& GraphBuilder, FBatchRenderContext const& InContext) { FRenderGraphSetup const& GraphSetup = InContext.GraphSetup; if (GraphSetup.bCompressPass && GraphSetup.bDirectAliasing) { CompressPages(GraphBuilder, InContext); } if (!GraphSetup.bDirectAliasing) { CopyPagesToOutput(GraphBuilder, InContext); } } void RenderPages(FRDGBuilder& GraphBuilder, FRenderPageBatchDesc const& InDesc) { FBatchRenderContext Context; Context.GraphSetup.Init(GraphBuilder, FRenderGraphSetup::FInitDesc(InDesc)); Context.BatchDesc = InDesc; // Disable MDC caching for this standalone path because we can't guarantee that primitives // associated with the scene have been recreated (e.g. by World->SendAllEndOfFrameUpdates()). Context.bAllowCachedMeshDrawCommands = false; RenderPageBatch(GraphBuilder, Context); FinalizePageBatch(GraphBuilder, Context); } /** This function is deprecated! */ uint32 GetRuntimeVirtualTextureSceneIndex_GameThread(URuntimeVirtualTextureComponent* InComponent) { if (InComponent == nullptr || InComponent->GetScene() == nullptr || InComponent->GetVirtualTexture() == nullptr) { return ~0u; } int32 SceneIndex = 0; FSceneInterface* SceneInterface = InComponent->GetScene(); int32 RuntimeVirtualTextureId = InComponent->GetVirtualTexture()->GetUniqueID(); ENQUEUE_RENDER_COMMAND(GetSceneIndexCommand)( [&SceneIndex, SceneInterface, RuntimeVirtualTextureId](FRHICommandListImmediate& RHICmdList) { if (FScene* Scene = SceneInterface->GetRenderScene()) { SceneIndex = Scene->RuntimeVirtualTextures.IndexOfByPredicate([RuntimeVirtualTextureId] (FRuntimeVirtualTextureSceneProxy const* SceneProxy) { return SceneProxy->RuntimeVirtualTextureId == RuntimeVirtualTextureId; }); } }); FlushRenderingCommands(); return SceneIndex; } }