1896 lines
70 KiB
C++
1896 lines
70 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "SlateRHIRenderingPolicy.h"
|
|
#include "UniformBuffer.h"
|
|
#include "Shader.h"
|
|
#include "ShowFlags.h"
|
|
#include "Misc/ConfigCacheIni.h"
|
|
#include "Misc/App.h"
|
|
#include "EngineGlobals.h"
|
|
#include "RHIStaticStates.h"
|
|
#include "RHIUtilities.h"
|
|
#include "GlobalRenderResources.h"
|
|
#include "Interfaces/SlateRHIRenderingPolicyInterface.h"
|
|
#include "SceneView.h"
|
|
#include "SceneUtils.h"
|
|
#include "Engine/Engine.h"
|
|
#include "SlateShaders.h"
|
|
#include "Rendering/SlateRenderer.h"
|
|
#include "SlateRHIRenderer.h"
|
|
#include "SlateMaterialShader.h"
|
|
#include "SlateUTextureResource.h"
|
|
#include "SlateMaterialResource.h"
|
|
#include "SlateUpdatableBuffer.h"
|
|
#include "SlatePostProcessor.h"
|
|
#include "Materials/MaterialRenderProxy.h"
|
|
#include "Modules/ModuleManager.h"
|
|
#include "PipelineStateCache.h"
|
|
#include "Math/RandomStream.h"
|
|
#include "DeviceProfiles/DeviceProfile.h"
|
|
#include "DeviceProfiles/DeviceProfileManager.h"
|
|
#include "Types/SlateConstants.h"
|
|
#include "RenderGraphUtils.h"
|
|
#include "SceneRenderTargetParameters.h"
|
|
#include "SceneInterface.h"
|
|
#include "Containers/StaticBitArray.h"
|
|
#include "MeshPassProcessor.h"
|
|
#include "PSOPrecacheValidation.h"
|
|
#include "VT/VirtualTextureFeedbackResource.h"
|
|
|
|
extern void UpdateNoiseTextureParameters(FViewUniformShaderParameters& ViewUniformShaderParameters);
|
|
|
|
DECLARE_CYCLE_STAT(TEXT("Update Buffers RT"), STAT_SlateUpdateBufferRTTime, STATGROUP_Slate);
|
|
DECLARE_CYCLE_STAT(TEXT("Update Buffers RT"), STAT_SlateUpdateBufferRTTimeLambda, STATGROUP_Slate);
|
|
|
|
DECLARE_DWORD_COUNTER_STAT(TEXT("Num Layers"), STAT_SlateNumLayers, STATGROUP_Slate);
|
|
DECLARE_DWORD_COUNTER_STAT(TEXT("Num Batches"), STAT_SlateNumBatches, STATGROUP_Slate);
|
|
DECLARE_DWORD_COUNTER_STAT(TEXT("Num Vertices"), STAT_SlateVertexCount, STATGROUP_Slate);
|
|
|
|
DECLARE_DWORD_COUNTER_STAT(TEXT("Clips (Scissor)"), STAT_SlateScissorClips, STATGROUP_Slate);
|
|
DECLARE_DWORD_COUNTER_STAT(TEXT("Clips (Stencil)"), STAT_SlateStencilClips, STATGROUP_Slate);
|
|
|
|
static int32 GSlateMaterialPSOPrecache = 1;
|
|
static FAutoConsoleVariableRef CVarGSlateMaterialPSOPrecache(
|
|
TEXT("r.PSOPrecache.SlateMaterials"),
|
|
GSlateMaterialPSOPrecache,
|
|
TEXT("Precache all possible required PSOs for loaded Slate Materials."),
|
|
ECVF_ReadOnly);
|
|
|
|
static const TCHAR* SlateGlobalPSOCollectorName = TEXT("SlateGlobalPSOCollector");
|
|
static const TCHAR* SlateMaterialPSOCollectorName = TEXT("SlateMaterialPSOCollector");
|
|
|
|
#if WITH_SLATE_DEBUGGING
|
|
static int32 GSlateEnableDrawEvents = 1;
|
|
#else
|
|
static int32 GSlateEnableDrawEvents = 0;
|
|
#endif
|
|
static FAutoConsoleVariableRef CVarGSlateEnableDrawEvents(TEXT("Slate.EnableDrawEvents"), GSlateEnableDrawEvents, TEXT("."), ECVF_Default);
|
|
|
|
#define WITH_SLATE_DRAW_EVENTS !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
|
|
#if WITH_SLATE_DRAW_EVENTS
|
|
#define SLATE_DRAW_EVENT( RHICmdList, EventName ) SCOPED_CONDITIONAL_DRAW_EVENT( RHICmdList, EventName, (GSlateEnableDrawEvents != 0));
|
|
#define SLATE_DRAW_EVENTF(RHICmdList, EventName, Format, ...) SCOPED_CONDITIONAL_DRAW_EVENTF(RHICmdList, EventName, (GSlateEnableDrawEvents != 0), Format, ##__VA_ARGS__);
|
|
#else
|
|
#define SLATE_DRAW_EVENT( RHICmdList, EventName )
|
|
#define SLATE_DRAW_EVENTF(RHICmdList, EventName, Format, ...)
|
|
#endif
|
|
|
|
#if WITH_SLATE_VISUALIZERS
|
|
extern TAutoConsoleVariable<int32> CVarShowSlateOverdraw;
|
|
extern TAutoConsoleVariable<int32> CVarShowSlateBatching;
|
|
#endif
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
FSlateRHIRenderingPolicy::FSlateRHIRenderingPolicy(TSharedRef<FSlateFontServices> InSlateFontServices, TSharedRef<FSlateRHIResourceManager> InResourceManager)
|
|
: FSlateRenderingPolicy(InSlateFontServices, 0)
|
|
, ResourceManager(InResourceManager)
|
|
{}
|
|
|
|
void FSlateRHIRenderingPolicy::AddSceneAt(FSceneInterface* Scene, int32 Index)
|
|
{
|
|
ResourceManager->AddSceneAt(Scene, Index);
|
|
}
|
|
|
|
void FSlateRHIRenderingPolicy::ClearScenes()
|
|
{
|
|
ResourceManager->ClearScenes();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
TConstArrayView<FTextureLODGroup> GetTextureLODGroups()
|
|
{
|
|
if (UDeviceProfileManager::DeviceProfileManagerSingleton)
|
|
{
|
|
if (UDeviceProfile* Profile = UDeviceProfileManager::DeviceProfileManagerSingleton->GetActiveProfile())
|
|
{
|
|
return Profile->GetTextureLODSettings()->TextureLODGroups;
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
ETextureSamplerFilter GetSamplerFilter(TConstArrayView<FTextureLODGroup> TextureLODGroups, const UTexture* Texture)
|
|
{
|
|
// Default to point filtering.
|
|
ETextureSamplerFilter Filter = ETextureSamplerFilter::Point;
|
|
|
|
switch (Texture->Filter)
|
|
{
|
|
case TF_Nearest:
|
|
Filter = ETextureSamplerFilter::Point;
|
|
break;
|
|
case TF_Bilinear:
|
|
Filter = ETextureSamplerFilter::Bilinear;
|
|
break;
|
|
case TF_Trilinear:
|
|
Filter = ETextureSamplerFilter::Trilinear;
|
|
break;
|
|
|
|
// TF_Default
|
|
default:
|
|
// Use LOD group value to find proper filter setting.
|
|
if (Texture->LODGroup < TextureLODGroups.Num())
|
|
{
|
|
Filter = TextureLODGroups[Texture->LODGroup].Filter;
|
|
}
|
|
}
|
|
|
|
return Filter;
|
|
}
|
|
|
|
FRHISamplerState* GetSamplerState(ESlateBatchDrawFlag DrawFlags, ETextureSamplerFilter Filter)
|
|
{
|
|
FRHISamplerState* SamplerState = nullptr;
|
|
|
|
if (EnumHasAllFlags(DrawFlags, (ESlateBatchDrawFlag::TileU | ESlateBatchDrawFlag::TileV)))
|
|
{
|
|
switch (Filter)
|
|
{
|
|
case ETextureSamplerFilter::Point:
|
|
SamplerState = TStaticSamplerState<SF_Point, AM_Wrap, AM_Wrap, AM_Wrap>::GetRHI();
|
|
break;
|
|
case ETextureSamplerFilter::AnisotropicPoint:
|
|
SamplerState = TStaticSamplerState<SF_AnisotropicPoint, AM_Wrap, AM_Wrap, AM_Wrap>::GetRHI();
|
|
break;
|
|
case ETextureSamplerFilter::Trilinear:
|
|
SamplerState = TStaticSamplerState<SF_Trilinear, AM_Wrap, AM_Wrap, AM_Wrap>::GetRHI();
|
|
break;
|
|
case ETextureSamplerFilter::AnisotropicLinear:
|
|
SamplerState = TStaticSamplerState<SF_AnisotropicLinear, AM_Wrap, AM_Wrap, AM_Wrap>::GetRHI();
|
|
break;
|
|
default:
|
|
SamplerState = TStaticSamplerState<SF_Bilinear, AM_Wrap, AM_Wrap, AM_Wrap>::GetRHI();
|
|
break;
|
|
}
|
|
}
|
|
else if (EnumHasAllFlags(DrawFlags, ESlateBatchDrawFlag::TileU))
|
|
{
|
|
switch (Filter)
|
|
{
|
|
case ETextureSamplerFilter::Point:
|
|
SamplerState = TStaticSamplerState<SF_Point, AM_Wrap, AM_Clamp, AM_Wrap>::GetRHI();
|
|
break;
|
|
case ETextureSamplerFilter::AnisotropicPoint:
|
|
SamplerState = TStaticSamplerState<SF_AnisotropicPoint, AM_Wrap, AM_Clamp, AM_Wrap>::GetRHI();
|
|
break;
|
|
case ETextureSamplerFilter::Trilinear:
|
|
SamplerState = TStaticSamplerState<SF_Trilinear, AM_Wrap, AM_Clamp, AM_Wrap>::GetRHI();
|
|
break;
|
|
case ETextureSamplerFilter::AnisotropicLinear:
|
|
SamplerState = TStaticSamplerState<SF_AnisotropicLinear, AM_Wrap, AM_Clamp, AM_Wrap>::GetRHI();
|
|
break;
|
|
default:
|
|
SamplerState = TStaticSamplerState<SF_Bilinear, AM_Wrap, AM_Clamp, AM_Wrap>::GetRHI();
|
|
break;
|
|
}
|
|
}
|
|
else if (EnumHasAllFlags(DrawFlags, ESlateBatchDrawFlag::TileV))
|
|
{
|
|
switch (Filter)
|
|
{
|
|
case ETextureSamplerFilter::Point:
|
|
SamplerState = TStaticSamplerState<SF_Point, AM_Clamp, AM_Wrap, AM_Wrap>::GetRHI();
|
|
break;
|
|
case ETextureSamplerFilter::AnisotropicPoint:
|
|
SamplerState = TStaticSamplerState<SF_AnisotropicPoint, AM_Clamp, AM_Wrap, AM_Wrap>::GetRHI();
|
|
break;
|
|
case ETextureSamplerFilter::Trilinear:
|
|
SamplerState = TStaticSamplerState<SF_Trilinear, AM_Clamp, AM_Wrap, AM_Wrap>::GetRHI();
|
|
break;
|
|
case ETextureSamplerFilter::AnisotropicLinear:
|
|
SamplerState = TStaticSamplerState<SF_AnisotropicLinear, AM_Clamp, AM_Wrap, AM_Wrap>::GetRHI();
|
|
break;
|
|
default:
|
|
SamplerState = TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Wrap, AM_Wrap>::GetRHI();
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (Filter)
|
|
{
|
|
case ETextureSamplerFilter::Point:
|
|
SamplerState = TStaticSamplerState<SF_Point, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
|
|
break;
|
|
case ETextureSamplerFilter::AnisotropicPoint:
|
|
SamplerState = TStaticSamplerState<SF_AnisotropicPoint, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
|
|
break;
|
|
case ETextureSamplerFilter::Trilinear:
|
|
SamplerState = TStaticSamplerState<SF_Trilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
|
|
break;
|
|
case ETextureSamplerFilter::AnisotropicLinear:
|
|
SamplerState = TStaticSamplerState<SF_AnisotropicLinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
|
|
break;
|
|
default:
|
|
SamplerState = TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
|
|
break;
|
|
}
|
|
}
|
|
|
|
return SamplerState;
|
|
}
|
|
|
|
/** Returns the pixel shader that should be used for the specified ShaderType and DrawEffects */
|
|
TShaderRef<FSlateElementPS> GetTexturePixelShader(FGlobalShaderMap* ShaderMap, ESlateShader ShaderType, ESlateDrawEffect DrawEffects, bool bUseTextureGrayscale, bool bIsVirtualTexture)
|
|
{
|
|
TShaderRef<FSlateElementPS> PixelShader;
|
|
|
|
#if WITH_SLATE_VISUALIZERS
|
|
if (CVarShowSlateOverdraw.GetValueOnAnyThread() != 0)
|
|
{
|
|
PixelShader = TShaderMapRef<FSlateDebugOverdrawPS>(ShaderMap);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
const bool bDrawDisabled = EnumHasAllFlags( DrawEffects, ESlateDrawEffect::DisabledEffect );
|
|
const bool bUseTextureAlpha = !EnumHasAllFlags( DrawEffects, ESlateDrawEffect::IgnoreTextureAlpha );
|
|
|
|
if (bDrawDisabled)
|
|
{
|
|
switch (ShaderType)
|
|
{
|
|
default:
|
|
case ESlateShader::Default:
|
|
if (bUseTextureAlpha)
|
|
{
|
|
if (bIsVirtualTexture)
|
|
{
|
|
if (bUseTextureGrayscale)
|
|
{
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::Default, true, true, true, true> >(ShaderMap);
|
|
}
|
|
else
|
|
{
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::Default, true, true, false, true> >(ShaderMap);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (bUseTextureGrayscale)
|
|
{
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::Default, true, true, true, false> >(ShaderMap);
|
|
}
|
|
else
|
|
{
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::Default, true, true, false, false> >(ShaderMap);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (bIsVirtualTexture)
|
|
{
|
|
if (bUseTextureGrayscale)
|
|
{
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::Default, true, false, true, true> >(ShaderMap);
|
|
}
|
|
else
|
|
{
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::Default, true, false, false, true> >(ShaderMap);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (bUseTextureGrayscale)
|
|
{
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::Default, true, false, true, false> >(ShaderMap);
|
|
}
|
|
else
|
|
{
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::Default, true, false, false, false> >(ShaderMap);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case ESlateShader::Border:
|
|
if ( bUseTextureAlpha )
|
|
{
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::Border, true, true> >(ShaderMap);
|
|
}
|
|
else
|
|
{
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::Border, true, false> >(ShaderMap);
|
|
}
|
|
break;
|
|
case ESlateShader::GrayscaleFont:
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::GrayscaleFont, true> >(ShaderMap);
|
|
break;
|
|
case ESlateShader::ColorFont:
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::ColorFont, true> >(ShaderMap);
|
|
break;
|
|
case ESlateShader::LineSegment:
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::LineSegment, true> >(ShaderMap);
|
|
break;
|
|
case ESlateShader::RoundedBox:
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::RoundedBox, true> >(ShaderMap);
|
|
break;
|
|
case ESlateShader::SdfFont:
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::SdfFont, true> >(ShaderMap);
|
|
break;
|
|
case ESlateShader::MsdfFont:
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::MsdfFont, true> >(ShaderMap);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (ShaderType)
|
|
{
|
|
default:
|
|
case ESlateShader::Default:
|
|
if (bUseTextureAlpha)
|
|
{
|
|
if (bIsVirtualTexture)
|
|
{
|
|
if (bUseTextureGrayscale)
|
|
{
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::Default, false, true, true, true> >(ShaderMap);
|
|
}
|
|
else
|
|
{
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::Default, false, true, false, true> >(ShaderMap);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (bUseTextureGrayscale)
|
|
{
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::Default, false, true, true, false> >(ShaderMap);
|
|
}
|
|
else
|
|
{
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::Default, false, true, false, false> >(ShaderMap);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (bIsVirtualTexture)
|
|
{
|
|
if (bUseTextureGrayscale)
|
|
{
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::Default, false, false, true, true> >(ShaderMap);
|
|
}
|
|
else
|
|
{
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::Default, false, false, false, true> >(ShaderMap);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (bUseTextureGrayscale)
|
|
{
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::Default, false, false, true, false> >(ShaderMap);
|
|
}
|
|
else
|
|
{
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::Default, false, false, false, false> >(ShaderMap);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case ESlateShader::Border:
|
|
if (bUseTextureAlpha)
|
|
{
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::Border, false, true> >(ShaderMap);
|
|
}
|
|
else
|
|
{
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::Border, false, false> >(ShaderMap);
|
|
}
|
|
break;
|
|
case ESlateShader::GrayscaleFont:
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::GrayscaleFont, false> >(ShaderMap);
|
|
break;
|
|
case ESlateShader::ColorFont:
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::ColorFont, false> >(ShaderMap);
|
|
break;
|
|
case ESlateShader::LineSegment:
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::LineSegment, false> >(ShaderMap);
|
|
break;
|
|
case ESlateShader::RoundedBox:
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::RoundedBox, false> >(ShaderMap);
|
|
break;
|
|
case ESlateShader::SdfFont:
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::SdfFont, false> >(ShaderMap);
|
|
break;
|
|
case ESlateShader::MsdfFont:
|
|
PixelShader = TShaderMapRef<TSlateElementPS<ESlateShader::MsdfFont, false> >(ShaderMap);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return PixelShader;
|
|
}
|
|
|
|
bool ChooseMaterialShaderTypes(ESlateShader ShaderType, bool bUseInstancing, FMaterialShaderTypes& OutShaderTypes)
|
|
{
|
|
switch (ShaderType)
|
|
{
|
|
case ESlateShader::Default:
|
|
OutShaderTypes.AddShaderType<TSlateMaterialShaderPS<ESlateShader::Default>>();
|
|
break;
|
|
case ESlateShader::Border:
|
|
OutShaderTypes.AddShaderType<TSlateMaterialShaderPS<ESlateShader::Border>>();
|
|
break;
|
|
case ESlateShader::GrayscaleFont:
|
|
OutShaderTypes.AddShaderType<TSlateMaterialShaderPS<ESlateShader::GrayscaleFont>>();
|
|
break;
|
|
case ESlateShader::ColorFont:
|
|
OutShaderTypes.AddShaderType<TSlateMaterialShaderPS<ESlateShader::ColorFont>>();
|
|
break;
|
|
case ESlateShader::Custom:
|
|
OutShaderTypes.AddShaderType<TSlateMaterialShaderPS<ESlateShader::Custom>>();
|
|
break;
|
|
case ESlateShader::RoundedBox:
|
|
OutShaderTypes.AddShaderType<TSlateMaterialShaderPS<ESlateShader::RoundedBox>>();
|
|
break;
|
|
case ESlateShader::SdfFont:
|
|
OutShaderTypes.AddShaderType<TSlateMaterialShaderPS<ESlateShader::SdfFont>>();
|
|
break;
|
|
case ESlateShader::MsdfFont:
|
|
OutShaderTypes.AddShaderType<TSlateMaterialShaderPS<ESlateShader::MsdfFont>>();
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
if (bUseInstancing)
|
|
{
|
|
OutShaderTypes.AddShaderType<TSlateMaterialShaderVS<true>>();
|
|
}
|
|
else
|
|
{
|
|
OutShaderTypes.AddShaderType<TSlateMaterialShaderVS<false>>();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
EPrimitiveType GetRHIPrimitiveType(ESlateDrawPrimitive SlateType)
|
|
{
|
|
switch(SlateType)
|
|
{
|
|
case ESlateDrawPrimitive::LineList:
|
|
return PT_LineList;
|
|
case ESlateDrawPrimitive::TriangleList:
|
|
default:
|
|
return PT_TriangleList;
|
|
}
|
|
};
|
|
|
|
FRHIBlendState* GetMaterialBlendState(FSlateShaderResource* TextureMaskResource, const FMaterial* Material)
|
|
{
|
|
if (TextureMaskResource && IsOpaqueOrMaskedBlendMode(*Material))
|
|
{
|
|
// Font materials require some form of translucent blending
|
|
return TStaticBlendState<CW_RGBA, BO_Add, BF_SourceAlpha, BF_InverseSourceAlpha, BO_Add, BF_InverseDestAlpha, BF_One>::GetRHI();
|
|
}
|
|
else
|
|
{
|
|
switch (Material->GetBlendMode())
|
|
{
|
|
default:
|
|
case BLEND_Opaque:
|
|
return TStaticBlendState<>::GetRHI();
|
|
case BLEND_Masked:
|
|
return TStaticBlendState<>::GetRHI();
|
|
case BLEND_Translucent:
|
|
return TStaticBlendState<CW_RGBA, BO_Add, BF_SourceAlpha, BF_InverseSourceAlpha, BO_Add, BF_InverseDestAlpha, BF_One>::GetRHI();
|
|
case BLEND_Additive:
|
|
// Add to the existing scene color
|
|
return TStaticBlendState<CW_RGBA, BO_Add, BF_One, BF_One, BO_Add, BF_One, BF_One>::GetRHI();
|
|
case BLEND_Modulate:
|
|
// Modulate with the existing scene color
|
|
return TStaticBlendState<CW_RGB, BO_Add, BF_Zero, BF_SourceColor>::GetRHI();
|
|
case BLEND_AlphaComposite:
|
|
// Blend with existing scene color. New color is already pre-multiplied by alpha.
|
|
return TStaticBlendState<CW_RGBA, BO_Add, BF_One, BF_InverseSourceAlpha, BO_Add, BF_One, BF_InverseSourceAlpha>::GetRHI();
|
|
case BLEND_AlphaHoldout:
|
|
// Blend by holding out the matte shape of the source alpha
|
|
return TStaticBlendState<CW_RGBA, BO_Add, BF_Zero, BF_InverseSourceAlpha, BO_Add, BF_Zero, BF_InverseSourceAlpha>::GetRHI();
|
|
};
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
FSlateElementsBuffers BuildSlateElementsBuffers(FRDGBuilder& GraphBuilder, FSlateBatchData& BatchData)
|
|
{
|
|
FSlateElementsBuffers ElementsBuffers;
|
|
|
|
if (BatchData.GetRenderBatches().IsEmpty())
|
|
{
|
|
return ElementsBuffers;
|
|
}
|
|
|
|
{
|
|
const FSlateVertexArray& Data = BatchData.GetFinalVertexData();
|
|
|
|
FRDGBufferDesc BufferDesc;
|
|
BufferDesc.Usage = EBufferUsageFlags::VertexBuffer | EBufferUsageFlags::Volatile;
|
|
BufferDesc.BytesPerElement = sizeof(Data[0]);
|
|
BufferDesc.NumElements = BatchData.GetMaxNumFinalVertices();
|
|
|
|
if (BufferDesc.NumElements > 0)
|
|
{
|
|
ElementsBuffers.VertexBuffer = GraphBuilder.CreateBuffer(BufferDesc, TEXT("SlateElementsVertexBuffer"));
|
|
GraphBuilder.QueueBufferUpload(ElementsBuffers.VertexBuffer, Data.GetData(), Data.Num() * BufferDesc.BytesPerElement, ERDGInitialDataFlags::NoCopy);
|
|
}
|
|
}
|
|
|
|
{
|
|
const FSlateIndexArray& Data = BatchData.GetFinalIndexData();
|
|
|
|
FRDGBufferDesc BufferDesc;
|
|
BufferDesc.Usage = EBufferUsageFlags::IndexBuffer | EBufferUsageFlags::Volatile;
|
|
BufferDesc.BytesPerElement = sizeof(Data[0]);
|
|
BufferDesc.NumElements = BatchData.GetMaxNumFinalIndices();
|
|
|
|
if (BufferDesc.NumElements > 0)
|
|
{
|
|
ElementsBuffers.IndexBuffer = GraphBuilder.CreateBuffer(BufferDesc, TEXT("SlateElementIndexBuffer"));
|
|
GraphBuilder.QueueBufferUpload(ElementsBuffers.IndexBuffer, Data.GetData(), Data.Num() * BufferDesc.BytesPerElement, ERDGInitialDataFlags::NoCopy);
|
|
}
|
|
}
|
|
|
|
SET_DWORD_STAT(STAT_SlateNumLayers, BatchData.GetNumLayers());
|
|
SET_DWORD_STAT(STAT_SlateNumBatches, BatchData.GetNumFinalBatches());
|
|
SET_DWORD_STAT(STAT_SlateVertexCount, BatchData.GetFinalVertexData().Num());
|
|
|
|
return ElementsBuffers;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
BEGIN_UNIFORM_BUFFER_STRUCT(FSlateViewUniformParameters, )
|
|
SHADER_PARAMETER(FMatrix44f, ViewProjection)
|
|
END_UNIFORM_BUFFER_STRUCT()
|
|
|
|
IMPLEMENT_STATIC_UNIFORM_BUFFER_SLOT(SlateView);
|
|
IMPLEMENT_STATIC_UNIFORM_BUFFER_STRUCT(FSlateViewUniformParameters, "SlateView", SlateView);
|
|
|
|
struct FSlateSceneViewAllocateInputs
|
|
{
|
|
FIntPoint TextureExtent = FIntPoint::ZeroValue;
|
|
FIntRect ViewRect;
|
|
FMatrix44f ViewProjectionMatrix = FMatrix44f::Identity;
|
|
FIntPoint CursorPosition = FIntPoint::ZeroValue;
|
|
FGameTime Time;
|
|
float ViewportScaleUI = 1.0f;
|
|
};
|
|
|
|
struct FSlateSceneView
|
|
{
|
|
const FSceneInterface* Scene = nullptr;
|
|
ERHIFeatureLevel::Type FeatureLevel = ERHIFeatureLevel::Num;
|
|
};
|
|
|
|
class FSlateSceneViewAllocator
|
|
{
|
|
RDG_FRIEND_ALLOCATOR_FRIEND(FSlateSceneViewAllocator);
|
|
public:
|
|
static FSlateSceneViewAllocator* Create(FRDGBuilder& GraphBuilder, FSlateRHIResourceManager& ResourceManager, const FSlateSceneViewAllocateInputs& Inputs)
|
|
{
|
|
return GraphBuilder.AllocObject<FSlateSceneViewAllocator>(ResourceManager, Inputs);
|
|
}
|
|
|
|
const TUniformBufferRef<FViewUniformShaderParameters>& GetViewUniformBuffer(const FSlateSceneView* View) const
|
|
{
|
|
return UniformBuffers[(int32)View->FeatureLevel];
|
|
}
|
|
|
|
const FSlateSceneView* BeginAllocateSceneView(FRDGBuilder& GraphBuilder, int32 SceneViewIndex)
|
|
{
|
|
if (!SceneViews.IsValidIndex(SceneViewIndex))
|
|
{
|
|
SceneViewIndex = SceneViewWithNullSceneIndex;
|
|
}
|
|
|
|
const FSlateSceneView& SceneView = SceneViews[SceneViewIndex];
|
|
const ERHIFeatureLevel::Type FeatureLevel = SceneView.FeatureLevel;
|
|
|
|
if (TUniformBufferRef<FViewUniformShaderParameters>& UniformBuffer = UniformBuffers[(int32)FeatureLevel]; !UniformBuffer)
|
|
{
|
|
UniformBuffer = CreateUniformBuffer(FeatureLevel, AllocateInputs);
|
|
}
|
|
|
|
return &SceneView;
|
|
}
|
|
|
|
private:
|
|
FSlateSceneViewAllocator(FSlateRHIResourceManager& ResourceManager, const FSlateSceneViewAllocateInputs& Inputs)
|
|
: SceneViewWithNullSceneIndex(ResourceManager.GetSceneCount())
|
|
, NumScenes(SceneViewWithNullSceneIndex + 1)
|
|
, AllocateInputs(Inputs)
|
|
{
|
|
SceneViews.SetNum(NumScenes);
|
|
SceneViews.Last().FeatureLevel = GMaxRHIFeatureLevel;
|
|
|
|
for (int32 Index = 0; Index < SceneViewWithNullSceneIndex; ++Index)
|
|
{
|
|
const FSceneInterface* Scene = ResourceManager.GetSceneAt(Index);
|
|
SceneViews[Index].Scene = Scene;
|
|
SceneViews[Index].FeatureLevel = Scene->GetFeatureLevel();
|
|
}
|
|
}
|
|
|
|
static TUniformBufferRef<FViewUniformShaderParameters> CreateUniformBuffer(ERHIFeatureLevel::Type FeatureLevel, const FSlateSceneViewAllocateInputs& Inputs)
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(FSlateSceneViewAllocator::CreateUniformBuffer);
|
|
|
|
static const FEngineShowFlags DefaultShowFlags(ESFIM_Game);
|
|
|
|
FIntRect ViewRect = Inputs.ViewRect;
|
|
|
|
// The window we are rendering to might not have a viewport, so use the full output instead.
|
|
if (ViewRect.IsEmpty())
|
|
{
|
|
ViewRect.Max = Inputs.TextureExtent;
|
|
}
|
|
|
|
FViewMatrices::FMinimalInitializer Initializer;
|
|
Initializer.ProjectionMatrix = FMatrix(Inputs.ViewProjectionMatrix);
|
|
Initializer.ConstrainedViewRect = ViewRect;
|
|
|
|
const FViewMatrices ViewMatrices(Initializer);
|
|
|
|
const FSetupViewUniformParametersInputs SetupViewUniformParameterInputs =
|
|
{
|
|
.EngineShowFlags = &DefaultShowFlags
|
|
, .UnscaledViewRect = ViewRect
|
|
, .Time = Inputs.Time
|
|
, .CursorPosition = Inputs.CursorPosition
|
|
};
|
|
|
|
FViewUniformShaderParameters ViewUniformShaderParameters;
|
|
|
|
SetupCommonViewUniformBufferParameters(ViewUniformShaderParameters, Inputs.TextureExtent, 1, ViewRect, ViewMatrices, ViewMatrices, SetupViewUniformParameterInputs);
|
|
|
|
// Update Viewport Scale UI from any external sources (Material editor, UMG zoom scale / etc).
|
|
ViewUniformShaderParameters.ViewportScaleUI = Inputs.ViewportScaleUI;
|
|
|
|
// Always Update cursor position in realtime for slate
|
|
ViewUniformShaderParameters.CursorPosition = Inputs.CursorPosition;
|
|
|
|
// Slate materials need this scale to be positive, otherwise it can fail in querying scene textures (e.g., custom stencil)
|
|
ViewUniformShaderParameters.BufferToSceneTextureScale = FVector2f(1.0f, 1.0f);
|
|
|
|
ViewUniformShaderParameters.MobilePreviewMode = (FeatureLevel == ERHIFeatureLevel::ES3_1) && GMaxRHIFeatureLevel > ERHIFeatureLevel::ES3_1 ? 1.0f : 0.0f;
|
|
|
|
UpdateNoiseTextureParameters(ViewUniformShaderParameters);
|
|
|
|
VirtualTexture::FFeedbackShaderParams VirtualTextureFeedbackShaderParams;
|
|
VirtualTexture::GetFeedbackShaderParams(VirtualTextureFeedbackShaderParams);
|
|
VirtualTexture::UpdateViewUniformShaderParameters(VirtualTextureFeedbackShaderParams, ViewUniformShaderParameters);
|
|
|
|
return TUniformBufferRef<FViewUniformShaderParameters>::CreateUniformBufferImmediate(ViewUniformShaderParameters, UniformBuffer_SingleFrame);
|
|
}
|
|
|
|
const int32 SceneViewWithNullSceneIndex;
|
|
const int32 NumScenes;
|
|
const FSlateSceneViewAllocateInputs AllocateInputs;
|
|
TArray<FSlateSceneView, FRDGArrayAllocator> SceneViews;
|
|
TStaticArray<TUniformBufferRef<FViewUniformShaderParameters>, (int32)ERHIFeatureLevel::Num> UniformBuffers;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
bool GetSlateClippingPipelineState(const FSlateClippingOp* ClippingStateOp, FRHIDepthStencilState*& OutDepthStencilState, uint8& OutStencilRef)
|
|
{
|
|
if (ClippingStateOp && ClippingStateOp->Method == EClippingMethod::Stencil)
|
|
{
|
|
// Setup the stenciling state to be read only now, disable depth writes, and restore the color buffer
|
|
// because we're about to go back to rendering widgets "normally", but with the added effect that now
|
|
// we have the stencil buffer bound with a bunch of clipping zones rendered into it.
|
|
OutDepthStencilState =
|
|
TStaticDepthStencilState<
|
|
/*bEnableDepthWrite*/ false
|
|
, /*DepthTest*/ CF_Always
|
|
, /*bEnableFrontFaceStencil*/ true
|
|
, /*FrontFaceStencilTest*/ CF_Equal
|
|
, /*FrontFaceStencilFailStencilOp*/ SO_Keep
|
|
, /*FrontFaceDepthFailStencilOp*/ SO_Keep
|
|
, /*FrontFacePassStencilOp*/ SO_Keep
|
|
, /*bEnableBackFaceStencil*/ true
|
|
, /*BackFaceStencilTest*/ CF_Equal
|
|
, /*BackFaceStencilFailStencilOp*/ SO_Keep
|
|
, /*BackFaceDepthFailStencilOp*/ SO_Keep
|
|
, /*BackFacePassStencilOp*/ SO_Keep
|
|
, /*StencilReadMask*/ 0xFF
|
|
, /*StencilWriteMask*/ 0xFF>::GetRHI();
|
|
|
|
// Set a StencilRef equal to the number of stenciling/clipping masks, so unless the pixel we're rendering
|
|
// to is on top of a stencil pixel with the same number it's going to get rejected, thereby clipping
|
|
// everything except for the cross-section of all the stenciling quads.
|
|
OutStencilRef = ClippingStateOp->MaskingId + ClippingStateOp->Data_Stencil.Zones.Num();
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
OutDepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
|
|
OutStencilRef = 0;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void SetSlateClipping(FRHICommandList& RHICmdList, const FSlateClippingOp* ClippingStateOp, FIntRect ViewportRect)
|
|
{
|
|
check(RHICmdList.IsInsideRenderPass());
|
|
|
|
if (ClippingStateOp)
|
|
{
|
|
const FVector2f ElementOffset = ClippingStateOp->Offset;
|
|
|
|
const auto ClampRectToViewport = [ElementOffset, ViewportRect] (FSlateRect ScissorRect)
|
|
{
|
|
ScissorRect.Left = FMath::Clamp(ScissorRect.Left + ElementOffset.X, (float)ViewportRect.Min.X, (float)ViewportRect.Max.X);
|
|
ScissorRect.Top = FMath::Clamp(ScissorRect.Top + ElementOffset.Y, (float)ViewportRect.Min.Y, (float)ViewportRect.Max.Y);
|
|
ScissorRect.Right = FMath::Clamp(ScissorRect.Right + ElementOffset.X, ScissorRect.Left, (float)ViewportRect.Max.X);
|
|
ScissorRect.Bottom = FMath::Clamp(ScissorRect.Bottom + ElementOffset.Y, ScissorRect.Top, (float)ViewportRect.Max.Y);
|
|
return ScissorRect;
|
|
};
|
|
|
|
if (ClippingStateOp->Method == EClippingMethod::Scissor)
|
|
{
|
|
const FSlateRect ScissorRect = ClampRectToViewport(ClippingStateOp->Data_Scissor.Rect);
|
|
RHICmdList.SetScissorRect(true, ScissorRect.Left, ScissorRect.Top, ScissorRect.Right, ScissorRect.Bottom);
|
|
}
|
|
else
|
|
{
|
|
check(ClippingStateOp->Method == EClippingMethod::Stencil);
|
|
|
|
const TConstArrayView<FSlateClippingZone> Zones = ClippingStateOp->Data_Stencil.Zones;
|
|
check(Zones.Num() > 0);
|
|
|
|
// There might be some large - useless stencils, especially in the first couple of stencils if large
|
|
// widgets that clip also contain render targets, so, by setting the scissor to the AABB of the final
|
|
// stencil, we can cut out a lot of work that can't possibly be useful. We also round it, because if we
|
|
// don't it can over-eagerly slice off pixels it shouldn't.
|
|
|
|
const FSlateRect ScissorRect = ClampRectToViewport(Zones.Last().GetBoundingBox().Round());
|
|
RHICmdList.SetScissorRect(true, ScissorRect.Left, ScissorRect.Top, ScissorRect.Right, ScissorRect.Bottom);
|
|
|
|
const uint8 MaskingId = ClippingStateOp->MaskingId;
|
|
|
|
FGlobalShaderMap* ShaderMap = GetGlobalShaderMap(GMaxRHIShaderPlatform);
|
|
TShaderMapRef<FSlateMaskingVS> VertexShader(ShaderMap);
|
|
TShaderMapRef<FSlateMaskingPS> PixelShader(ShaderMap);
|
|
|
|
// Start by setting up the stenciling states so that we can write representations of the clipping zones into the stencil buffer only.
|
|
FGraphicsPipelineStateInitializer WriteMaskPSOInit;
|
|
RHICmdList.ApplyCachedRenderTargets(WriteMaskPSOInit);
|
|
WriteMaskPSOInit.BlendState = TStaticBlendStateWriteMask<CW_NONE, CW_NONE, CW_NONE, CW_NONE, CW_NONE, CW_NONE, CW_NONE, CW_NONE>::GetRHI();
|
|
WriteMaskPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI();
|
|
WriteMaskPSOInit.DepthStencilState =
|
|
TStaticDepthStencilState<
|
|
/*bEnableDepthWrite*/ false
|
|
, /*DepthTest*/ CF_Always
|
|
, /*bEnableFrontFaceStencil*/ true
|
|
, /*FrontFaceStencilTest*/ CF_Always
|
|
, /*FrontFaceStencilFailStencilOp*/ SO_Keep
|
|
, /*FrontFaceDepthFailStencilOp*/ SO_Keep
|
|
, /*FrontFacePassStencilOp*/ SO_Replace
|
|
, /*bEnableBackFaceStencil*/ true
|
|
, /*BackFaceStencilTest*/ CF_Always
|
|
, /*BackFaceStencilFailStencilOp*/ SO_Keep
|
|
, /*BackFaceDepthFailStencilOp*/ SO_Keep
|
|
, /*BackFacePassStencilOp*/ SO_Replace
|
|
, /*StencilReadMask*/ 0xFF
|
|
, /*StencilWriteMask*/ 0xFF>::GetRHI();
|
|
|
|
WriteMaskPSOInit.BoundShaderState.VertexDeclarationRHI = GSlateMaskingVertexDeclaration.VertexDeclarationRHI;
|
|
WriteMaskPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
|
|
WriteMaskPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
|
|
WriteMaskPSOInit.PrimitiveType = PT_TriangleStrip;
|
|
|
|
// Draw the first stencil using SO_Replace, so that we stomp any pixel with a MaskingID + 1.
|
|
SetGraphicsPipelineState(RHICmdList, WriteMaskPSOInit, MaskingId + 1);
|
|
|
|
// Simple 2D orthographic projection from screen space to NDC space.
|
|
const FVector2f A
|
|
(
|
|
2.0f / ViewportRect.Width(),
|
|
2.0f / -ViewportRect.Height()
|
|
);
|
|
|
|
const FVector2f B
|
|
(
|
|
(ViewportRect.Min.X + ViewportRect.Max.X) / -ViewportRect.Width(),
|
|
(ViewportRect.Min.Y + ViewportRect.Max.Y) / ViewportRect.Height()
|
|
);
|
|
|
|
const auto TransformVertex = [A, B, ElementOffset](FVector2f P)
|
|
{
|
|
return FVector2f((P.X + ElementOffset.X) * A.X + B.X, (P.Y + ElementOffset.Y) * A.Y + B.Y);
|
|
};
|
|
|
|
const auto SetMaskingParameters = [VertexShader, TransformVertex] (FRHIBatchedShaderParameters& BatchedParameters, const FSlateClippingZone& Zone)
|
|
{
|
|
FSlateMaskingVS::FParameters Parameters;
|
|
Parameters.MaskRectPacked[0] = FVector4f(TransformVertex(Zone.TopLeft), TransformVertex(Zone.TopRight));
|
|
Parameters.MaskRectPacked[1] = FVector4f(TransformVertex(Zone.BottomLeft), TransformVertex(Zone.BottomRight));
|
|
SetShaderParameters(BatchedParameters, VertexShader, Parameters);
|
|
};
|
|
|
|
{
|
|
FRHIBatchedShaderParameters& BatchedParameters = RHICmdList.GetScratchShaderParameters();
|
|
SetMaskingParameters(BatchedParameters, Zones[0]);
|
|
RHICmdList.SetBatchedShaderParameters(VertexShader.GetVertexShader(), BatchedParameters);
|
|
RHICmdList.SetStreamSource(0, GSlateStencilClipVertexBuffer.VertexBufferRHI, 0);
|
|
RHICmdList.DrawPrimitive(0, 2, 1);
|
|
}
|
|
|
|
// Now setup the pipeline to use SO_SaturatedIncrement, since we've established the initial
|
|
// stencil with SO_Replace, we can safely use SO_SaturatedIncrement, to build up the stencil
|
|
// to the required mask of MaskingID + StencilQuads.Num(), thereby ensuring only the union of
|
|
// all stencils will render pixels.
|
|
WriteMaskPSOInit.DepthStencilState =
|
|
TStaticDepthStencilState<
|
|
/*bEnableDepthWrite*/ false
|
|
, /*DepthTest*/ CF_Always
|
|
, /*bEnableFrontFaceStencil*/ true
|
|
, /*FrontFaceStencilTest*/ CF_Always
|
|
, /*FrontFaceStencilFailStencilOp*/ SO_Keep
|
|
, /*FrontFaceDepthFailStencilOp*/ SO_Keep
|
|
, /*FrontFacePassStencilOp*/ SO_SaturatedIncrement
|
|
, /*bEnableBackFaceStencil*/ true
|
|
, /*BackFaceStencilTest*/ CF_Always
|
|
, /*BackFaceStencilFailStencilOp*/ SO_Keep
|
|
, /*BackFaceDepthFailStencilOp*/ SO_Keep
|
|
, /*BackFacePassStencilOp*/ SO_SaturatedIncrement
|
|
, /*StencilReadMask*/ 0xFF
|
|
, /*StencilWriteMask*/ 0xFF>::GetRHI();
|
|
|
|
SetGraphicsPipelineState(RHICmdList, WriteMaskPSOInit, 0);
|
|
|
|
// Next write the number of quads representing the number of clipping zones have on top of each other.
|
|
for (int32 MaskIndex = 1; MaskIndex < Zones.Num(); MaskIndex++)
|
|
{
|
|
FRHIBatchedShaderParameters& BatchedParameters = RHICmdList.GetScratchShaderParameters();
|
|
SetMaskingParameters(BatchedParameters, Zones[MaskIndex]);
|
|
RHICmdList.SetBatchedShaderParameters(VertexShader.GetVertexShader(), BatchedParameters);
|
|
RHICmdList.SetStreamSource(0, GSlateStencilClipVertexBuffer.VertexBufferRHI, 0);
|
|
RHICmdList.DrawPrimitive(0, 2, 1);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RHICmdList.SetScissorRect(false, 0.0f, 0.0f, 0.0f, 0.0f);
|
|
}
|
|
}
|
|
|
|
enum class ESlateClippingStencilAction : uint8
|
|
{
|
|
None,
|
|
Write,
|
|
Clear
|
|
};
|
|
|
|
struct FSlateClippingCreateContext
|
|
{
|
|
uint32 NumStencils = 0;
|
|
uint32 NumScissors = 0;
|
|
uint32 MaskingId = 0;
|
|
ESlateClippingStencilAction StencilAction = ESlateClippingStencilAction::None;
|
|
};
|
|
|
|
const FSlateClippingOp* CreateSlateClipping(FRDGBuilder& GraphBuilder, const FVector2f ElementsOffset, const FSlateClippingState* ClippingState, FSlateClippingCreateContext& Context)
|
|
{
|
|
Context.StencilAction = ESlateClippingStencilAction::None;
|
|
|
|
if (ClippingState)
|
|
{
|
|
if (ClippingState->GetClippingMethod() == EClippingMethod::Scissor)
|
|
{
|
|
Context.NumScissors++;
|
|
|
|
const FSlateClippingZone& ScissorRect = ClippingState->ScissorRect.GetValue();
|
|
|
|
return FSlateClippingOp::Scissor(GraphBuilder, ElementsOffset, FSlateRect(ScissorRect.TopLeft.X, ScissorRect.TopLeft.Y, ScissorRect.BottomRight.X, ScissorRect.BottomRight.Y));
|
|
}
|
|
else
|
|
{
|
|
Context.NumStencils++;
|
|
|
|
TConstArrayView<FSlateClippingZone> StencilQuads = ClippingState->StencilQuads;
|
|
check(StencilQuads.Num() > 0);
|
|
|
|
// Reset the masking ID back to zero if stencil is going to overflow.
|
|
if (Context.MaskingId + StencilQuads.Num() > 255)
|
|
{
|
|
Context.MaskingId = 0;
|
|
}
|
|
|
|
// Mark stencil for clear when the masking id is 0.
|
|
Context.StencilAction = Context.MaskingId == 0 ? ESlateClippingStencilAction::Clear : ESlateClippingStencilAction::Write;
|
|
|
|
const FSlateClippingOp* Op = FSlateClippingOp::Stencil(GraphBuilder, ElementsOffset, StencilQuads, Context.MaskingId);
|
|
Context.MaskingId += StencilQuads.Num();
|
|
return Op;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
enum class ESlateRenderBatchType
|
|
{
|
|
CustomDrawer,
|
|
PostProcess,
|
|
Primitive,
|
|
MAX
|
|
};
|
|
|
|
inline ESlateRenderBatchType GetSlateRenderBatchType(const FSlateRenderBatch& DrawBatch)
|
|
{
|
|
if (DrawBatch.CustomDrawer != nullptr)
|
|
{
|
|
return ESlateRenderBatchType::CustomDrawer;
|
|
}
|
|
|
|
if (DrawBatch.ShaderType == ESlateShader::PostProcess)
|
|
{
|
|
return ESlateRenderBatchType::PostProcess;
|
|
}
|
|
|
|
return ESlateRenderBatchType::Primitive;
|
|
}
|
|
|
|
class FSlateDrawShaderBindings : public FMeshDrawSingleShaderBindings
|
|
{
|
|
FSlateDrawShaderBindings(const TShaderRef<FShader>& InShader, const FMeshDrawShaderBindingsLayout& InLayout, uint8* InData)
|
|
: FMeshDrawSingleShaderBindings(InLayout, InData)
|
|
, Shader(InShader)
|
|
{}
|
|
|
|
public:
|
|
static FSlateDrawShaderBindings* Create(FRDGBuilder& GraphBuilder, const TShaderRef<FShader>& Shader)
|
|
{
|
|
const FMeshDrawShaderBindingsLayout Layout(Shader);
|
|
const uint32 DataSize = Layout.GetDataSizeBytes();
|
|
uint8* Data = (uint8*)GraphBuilder.Alloc(DataSize);
|
|
FMemory::Memzero(Data, DataSize);
|
|
return new (GraphBuilder.Alloc(sizeof(FSlateDrawShaderBindings))) FSlateDrawShaderBindings(Shader, Layout, Data);
|
|
}
|
|
|
|
void SetOnCommandList(FRHICommandList& RHICmdList) const
|
|
{
|
|
FRHIBatchedShaderParameters& BatchedParameters = RHICmdList.GetScratchShaderParameters();
|
|
FReadOnlyMeshDrawSingleShaderBindings::SetShaderBindings(BatchedParameters, FReadOnlyMeshDrawSingleShaderBindings(*this));
|
|
RHICmdList.SetBatchedShaderParameters(Shader.GetGraphicsShader(), BatchedParameters);
|
|
}
|
|
|
|
TShaderRef<FShader> Shader;
|
|
};
|
|
|
|
struct FSlateRenderBatchOp
|
|
{
|
|
FSlateRenderBatchOp* Next;
|
|
const FSlateRenderBatch* RenderBatch;
|
|
const FSlateClippingOp* ClippingStateOp;
|
|
FSlateDrawShaderBindings* VertexBindings;
|
|
FSlateDrawShaderBindings* PixelBindings;
|
|
FRHIBuffer* InstanceBuffer;
|
|
FRHIBlendState* BlendState;
|
|
ESlateShaderResource::Type ShaderResourceType;
|
|
#if WITH_SLATE_DRAW_EVENTS
|
|
const FString* MaterialName;
|
|
#endif
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
struct FSlateRenderBatchCreateInputs
|
|
{
|
|
FGlobalShaderMap* ShaderMap;
|
|
FSlateSceneViewAllocator* SceneViewAllocator;
|
|
TConstArrayView<FTextureLODGroup> TextureLODGroups;
|
|
float DisplayGamma;
|
|
float DisplayContrast;
|
|
float EngineGamma;
|
|
#if WITH_SLATE_VISUALIZERS
|
|
FLinearColor BatchColor;
|
|
#endif
|
|
};
|
|
|
|
struct FSlateRenderBatchDrawState
|
|
{
|
|
const FSlateClippingOp* LastClippingOp = nullptr;
|
|
FGraphicsPipelineStateInitializer GraphicsPSOInit;
|
|
uint8 StencilRef = 0;
|
|
};
|
|
|
|
FSlateRenderBatchOp* CreateSlateRenderBatchOp(
|
|
FRDGBuilder& GraphBuilder,
|
|
const FSlateRenderBatchCreateInputs& Inputs,
|
|
const FSlateRenderBatch* RenderBatch,
|
|
const FSlateClippingOp* ClippingStateOp)
|
|
{
|
|
const FSlateShaderResource* ShaderResource = RenderBatch->ShaderResource;
|
|
const ESlateBatchDrawFlag DrawFlags = RenderBatch->DrawFlags;
|
|
const ESlateDrawEffect DrawEffects = RenderBatch->DrawEffects;
|
|
const ESlateShader ShaderType = RenderBatch->ShaderType;
|
|
const FShaderParams& ShaderParams = RenderBatch->ShaderParams;
|
|
|
|
check(ShaderResource == nullptr || !ShaderResource->Debug_IsDestroyed());
|
|
const ESlateShaderResource::Type ResourceType = ShaderResource ? ShaderResource->GetType() : ESlateShaderResource::Invalid;
|
|
|
|
const bool bUseInstancing = RenderBatch->InstanceCount > 0 && RenderBatch->InstanceData != nullptr;
|
|
|
|
const float FinalGamma = EnumHasAnyFlags(DrawFlags, ESlateBatchDrawFlag::ReverseGamma) ? (1.0f / Inputs.EngineGamma) : EnumHasAnyFlags(DrawFlags, ESlateBatchDrawFlag::NoGamma) ? 1.0f : Inputs.DisplayGamma;
|
|
const float FinalContrast = EnumHasAnyFlags(DrawFlags, ESlateBatchDrawFlag::NoGamma) ? 1 : Inputs.DisplayContrast;
|
|
|
|
FRHIBlendState* BlendState = nullptr;
|
|
FSlateDrawShaderBindings* PixelBindings = nullptr;
|
|
FSlateDrawShaderBindings* VertexBindings = nullptr;
|
|
|
|
#if WITH_SLATE_DRAW_EVENTS
|
|
const FString* MaterialName = nullptr;
|
|
#endif
|
|
|
|
if (ResourceType == ESlateShaderResource::Material)
|
|
{
|
|
// Skip material render batches when the engine is not available.
|
|
if (!GEngine)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
FSlateMaterialResource* MaterialShaderResource = (FSlateMaterialResource*)ShaderResource;
|
|
MaterialShaderResource->CheckForStaleResources();
|
|
|
|
const FMaterialRenderProxy* MaterialRenderProxy = MaterialShaderResource->GetRenderProxy();
|
|
|
|
if (!MaterialRenderProxy)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
const FSlateSceneView* SceneView = Inputs.SceneViewAllocator->BeginAllocateSceneView(GraphBuilder, RenderBatch->SceneIndex);
|
|
const ERHIFeatureLevel::Type SceneFeatureLevel = SceneView->FeatureLevel;
|
|
const FSceneInterface* Scene = SceneView->Scene;
|
|
const TUniformBufferRef<FViewUniformShaderParameters>& ViewUniformBuffer = Inputs.SceneViewAllocator->GetViewUniformBuffer(SceneView);
|
|
|
|
TShaderRef<FSlateMaterialShaderVS> VertexShader;
|
|
TShaderRef<FSlateMaterialShaderPS> PixelShader;
|
|
|
|
FMaterialShaderTypes ShaderTypesToGet;
|
|
if (!ChooseMaterialShaderTypes(ShaderType, bUseInstancing, ShaderTypesToGet))
|
|
{
|
|
checkf(false, TEXT("Unsupported Slate shader type for use with materials"));
|
|
return nullptr;
|
|
}
|
|
const FMaterial* EffectiveMaterial = nullptr;
|
|
|
|
while (MaterialRenderProxy)
|
|
{
|
|
const FMaterial* Material = MaterialRenderProxy->UpdateUniformExpressionCacheIfNeeded(GraphBuilder.RHICmdList, SceneFeatureLevel);
|
|
FMaterialShaders Shaders;
|
|
if (Material && Material->TryGetShaders(ShaderTypesToGet, nullptr, Shaders))
|
|
{
|
|
EffectiveMaterial = Material;
|
|
Shaders.TryGetVertexShader(VertexShader);
|
|
Shaders.TryGetPixelShader(PixelShader);
|
|
break;
|
|
}
|
|
|
|
MaterialRenderProxy = MaterialRenderProxy->GetFallback(SceneFeatureLevel);
|
|
}
|
|
|
|
if (!VertexShader.IsValid() || !PixelShader.IsValid())
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
#if WITH_SLATE_DRAW_EVENTS
|
|
MaterialName = &MaterialRenderProxy->GetMaterialName();
|
|
#endif
|
|
|
|
VertexBindings = FSlateDrawShaderBindings::Create(GraphBuilder, VertexShader);
|
|
VertexShader->SetMaterialShaderParameters(*VertexBindings, Scene, ViewUniformBuffer, MaterialRenderProxy, EffectiveMaterial);
|
|
|
|
const bool bDrawDisabled = EnumHasAllFlags(RenderBatch->DrawEffects, ESlateDrawEffect::DisabledEffect);
|
|
|
|
PixelBindings = FSlateDrawShaderBindings::Create(GraphBuilder, PixelShader);
|
|
PixelShader->SetMaterialShaderParameters(*PixelBindings, Scene, ViewUniformBuffer, MaterialRenderProxy, EffectiveMaterial, ShaderParams);
|
|
PixelShader->SetDisplayGammaAndContrast(*PixelBindings, FinalGamma, FinalContrast);
|
|
PixelShader->SetDrawFlags(*PixelBindings, bDrawDisabled);
|
|
|
|
auto* MaskResource = static_cast<TSlateTexture<FTextureRHIRef>*>(MaterialShaderResource->GetTextureMaskResource());
|
|
|
|
if (MaskResource)
|
|
{
|
|
PixelShader->SetAdditionalTexture(*PixelBindings, MaskResource->GetTypedResource(), TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI());
|
|
}
|
|
|
|
BlendState = GetMaterialBlendState(MaskResource, EffectiveMaterial);
|
|
}
|
|
else
|
|
{
|
|
check(!bUseInstancing);
|
|
|
|
TShaderRef<FSlateElementPS> PixelShader;
|
|
|
|
#if WITH_SLATE_VISUALIZERS
|
|
TShaderRef<FSlateDebugBatchingPS> BatchingPixelShader;
|
|
if (CVarShowSlateBatching.GetValueOnRenderThread() != 0)
|
|
{
|
|
BatchingPixelShader = TShaderMapRef<FSlateDebugBatchingPS>(Inputs.ShaderMap);
|
|
PixelShader = BatchingPixelShader;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
bool bIsVirtualTexture = false;
|
|
|
|
// check if texture is using BC4 compression and set shader to render grayscale
|
|
// @todo Oodle : it seems strange to special-case only BC4/TC_Alpha here ; prefer calling ShouldUseGreyScaleEditorVisualization()
|
|
bool bUseTextureGrayscale = false;
|
|
|
|
if (ShaderResource != nullptr && ResourceType == ESlateShaderResource::TextureObject)
|
|
{
|
|
FSlateBaseUTextureResource* TextureObjectResource = const_cast<FSlateBaseUTextureResource*>(static_cast<const FSlateBaseUTextureResource*>(ShaderResource));
|
|
|
|
if (UTexture* TextureObj = TextureObjectResource->GetTextureObject())
|
|
{
|
|
bIsVirtualTexture = TextureObj->IsCurrentlyVirtualTextured();
|
|
|
|
if (TextureObj->CompressionSettings == TC_Alpha)
|
|
{
|
|
bUseTextureGrayscale = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
PixelShader = GetTexturePixelShader(Inputs.ShaderMap, ShaderType, DrawEffects, bUseTextureGrayscale, bIsVirtualTexture);
|
|
}
|
|
|
|
#if WITH_SLATE_VISUALIZERS
|
|
if (BatchingPixelShader.IsValid())
|
|
{
|
|
BlendState = TStaticBlendState<CW_RGBA, BO_Add, BF_SourceAlpha, BF_InverseSourceAlpha, BO_Add, BF_One, BF_InverseSourceAlpha>::GetRHI();
|
|
}
|
|
else if (CVarShowSlateOverdraw.GetValueOnRenderThread() != 0)
|
|
{
|
|
BlendState = TStaticBlendState<CW_RGB, BO_Add, BF_One, BF_One, BO_Add, BF_Zero, BF_InverseSourceAlpha>::GetRHI();
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
BlendState =
|
|
EnumHasAllFlags(DrawFlags, ESlateBatchDrawFlag::NoBlending)
|
|
? TStaticBlendState<>::GetRHI()
|
|
: (EnumHasAllFlags(DrawFlags, ESlateBatchDrawFlag::PreMultipliedAlpha)
|
|
? TStaticBlendState<CW_RGBA, BO_Add, BF_One, BF_InverseSourceAlpha, BO_Add, BF_One, BF_InverseSourceAlpha>::GetRHI()
|
|
: TStaticBlendState<CW_RGBA, BO_Add, BF_SourceAlpha, BF_InverseSourceAlpha, BO_Add, BF_One, BF_InverseSourceAlpha>::GetRHI())
|
|
;
|
|
}
|
|
|
|
FRHISamplerState* SamplerState = TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
|
|
FRHITexture* TextureRHI = GWhiteTexture->TextureRHI;
|
|
bool bIsVirtualTexture = false;
|
|
FTextureResource* TextureResource = nullptr;
|
|
|
|
if (ShaderResource)
|
|
{
|
|
ETextureSamplerFilter Filter = ETextureSamplerFilter::Bilinear;
|
|
|
|
if (ResourceType == ESlateShaderResource::TextureObject)
|
|
{
|
|
FSlateBaseUTextureResource* TextureObjectResource = (FSlateBaseUTextureResource*)ShaderResource;
|
|
if (UTexture* TextureObj = TextureObjectResource->GetTextureObject())
|
|
{
|
|
TextureObjectResource->CheckForStaleResources();
|
|
|
|
TextureRHI = TextureObjectResource->AccessRHIResource();
|
|
|
|
// This can upset some RHIs, so use transparent black texture until it's valid.
|
|
// these can be temporarily invalid when recreating them / invalidating their streaming
|
|
// state.
|
|
if (TextureRHI == nullptr)
|
|
{
|
|
// We use transparent black here, because it's about to become valid - probably, and flashing white
|
|
// wouldn't be ideal.
|
|
TextureRHI = GTransparentBlackTexture->TextureRHI;
|
|
}
|
|
|
|
TextureResource = TextureObj->GetResource();
|
|
|
|
Filter = GetSamplerFilter(Inputs.TextureLODGroups, TextureObj);
|
|
bIsVirtualTexture = TextureObj->IsCurrentlyVirtualTextured();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FRHITexture* NativeTextureRHI = ((TSlateTexture<FTextureRHIRef>*)ShaderResource)->GetTypedResource();
|
|
// Atlas textures that have no content are never initialized but null textures are invalid on many platforms.
|
|
TextureRHI = NativeTextureRHI ? NativeTextureRHI : (FRHITexture*)GWhiteTexture->TextureRHI;
|
|
}
|
|
|
|
SamplerState = GetSamplerState(DrawFlags, Filter);
|
|
}
|
|
|
|
PixelBindings = FSlateDrawShaderBindings::Create(GraphBuilder, PixelShader);
|
|
|
|
#if WITH_SLATE_VISUALIZERS
|
|
if (BatchingPixelShader.IsValid())
|
|
{
|
|
BatchingPixelShader->SetBatchColor(*PixelBindings, Inputs.BatchColor);
|
|
}
|
|
#endif
|
|
|
|
if (bIsVirtualTexture && TextureResource != nullptr)
|
|
{
|
|
PixelShader->SetVirtualTextureParameters(*PixelBindings, static_cast<FVirtualTexture2DResource*>(TextureResource));
|
|
}
|
|
else
|
|
{
|
|
PixelShader->SetTexture(*PixelBindings, TextureRHI, SamplerState);
|
|
}
|
|
|
|
PixelShader->SetShaderParams(*PixelBindings, ShaderParams);
|
|
PixelShader->SetDisplayGammaAndInvertAlphaAndContrast(*PixelBindings, FinalGamma, EnumHasAllFlags(DrawEffects, ESlateDrawEffect::InvertAlpha) ? 1.0f : 0.0f, FinalContrast);
|
|
}
|
|
|
|
FSlateRenderBatchOp* RenderBatchOp = GraphBuilder.AllocPOD<FSlateRenderBatchOp>();
|
|
RenderBatchOp->RenderBatch = RenderBatch;
|
|
RenderBatchOp->ClippingStateOp = ClippingStateOp;
|
|
RenderBatchOp->ShaderResourceType = ResourceType;
|
|
RenderBatchOp->VertexBindings = VertexBindings;
|
|
RenderBatchOp->PixelBindings = PixelBindings;
|
|
RenderBatchOp->InstanceBuffer = bUseInstancing ? RenderBatch->InstanceData->GetRHI() : nullptr;
|
|
RenderBatchOp->BlendState = BlendState;
|
|
RenderBatchOp->Next = nullptr;
|
|
#if WITH_SLATE_DRAW_EVENTS
|
|
RenderBatchOp->MaterialName = MaterialName;
|
|
#endif
|
|
return RenderBatchOp;
|
|
}
|
|
|
|
struct FSlateRenderBatchDrawInputs
|
|
{
|
|
FGlobalShaderMap* ShaderMap;
|
|
FSlateElementsBuffers ElementsBuffers;
|
|
FIntRect ElementsViewRect;
|
|
bool bWireframe;
|
|
};
|
|
|
|
void DrawSlateRenderBatch(
|
|
FRHICommandList& RHICmdList,
|
|
FSlateRenderBatchDrawState& State,
|
|
const FSlateRenderBatchDrawInputs& Inputs,
|
|
const FSlateRenderBatchOp& RenderBatchOp)
|
|
{
|
|
const FSlateClippingOp* ClippingStateOp = RenderBatchOp.ClippingStateOp;
|
|
const FSlateRenderBatch& RenderBatch = *RenderBatchOp.RenderBatch;
|
|
|
|
if (State.LastClippingOp != ClippingStateOp)
|
|
{
|
|
GetSlateClippingPipelineState(ClippingStateOp, State.GraphicsPSOInit.DepthStencilState, State.StencilRef);
|
|
SetSlateClipping(RHICmdList, ClippingStateOp, Inputs.ElementsViewRect);
|
|
State.LastClippingOp = ClippingStateOp;
|
|
}
|
|
|
|
FRHIBuffer* ElementsVertexBuffer = Inputs.ElementsBuffers.VertexBuffer->GetRHI();
|
|
FRHIBuffer* ElementsIndexBuffer = Inputs.ElementsBuffers.IndexBuffer->GetRHI();
|
|
|
|
State.GraphicsPSOInit.BlendState = RenderBatchOp.BlendState;
|
|
|
|
if (EnumHasAllFlags(RenderBatch.DrawFlags, ESlateBatchDrawFlag::Wireframe))
|
|
{
|
|
State.GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Wireframe>::GetRHI();
|
|
}
|
|
else
|
|
{
|
|
State.GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid>::GetRHI();
|
|
}
|
|
|
|
check(RenderBatch.NumIndices > 0);
|
|
const uint32 PrimitiveCount = RenderBatch.DrawPrimitiveType == ESlateDrawPrimitive::LineList ? RenderBatch.NumIndices / 2 : RenderBatch.NumIndices / 3;
|
|
|
|
if (RenderBatchOp.ShaderResourceType == ESlateShaderResource::Material)
|
|
{
|
|
SLATE_DRAW_EVENTF(RHICmdList, MaterialBatch, TEXT("Slate Material: %s"), *RenderBatchOp.MaterialName);
|
|
|
|
State.GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = RenderBatchOp.InstanceBuffer ? GSlateInstancedVertexDeclaration.VertexDeclarationRHI : GSlateVertexDeclaration.VertexDeclarationRHI;
|
|
State.GraphicsPSOInit.BoundShaderState.VertexShaderRHI = RenderBatchOp.VertexBindings->Shader.GetVertexShader();
|
|
State.GraphicsPSOInit.BoundShaderState.PixelShaderRHI = RenderBatchOp.PixelBindings->Shader.GetPixelShader();
|
|
State.GraphicsPSOInit.PrimitiveType = GetRHIPrimitiveType(RenderBatch.DrawPrimitiveType);
|
|
|
|
#if PSO_PRECACHING_VALIDATE
|
|
if (PSOCollectorStats::IsFullPrecachingValidationEnabled())
|
|
{
|
|
static const int32 MaterialPSOCollectorIndex = FPSOCollectorCreateManager::GetIndex(GetFeatureLevelShadingPath(GMaxRHIFeatureLevel), SlateMaterialPSOCollectorName);
|
|
// Material Render Proxy is not cached in the state, but could be done for better debugging if needed
|
|
PSOCollectorStats::CheckFullPipelineStateInCache(State.GraphicsPSOInit, EPSOPrecacheResult::Unknown, nullptr, nullptr, nullptr, MaterialPSOCollectorIndex);
|
|
}
|
|
#endif // PSO_PRECACHING_VALIDATE
|
|
|
|
SetGraphicsPipelineState(RHICmdList, State.GraphicsPSOInit, State.StencilRef);
|
|
|
|
RenderBatchOp.VertexBindings->SetOnCommandList(RHICmdList);
|
|
RenderBatchOp.PixelBindings->SetOnCommandList(RHICmdList);
|
|
|
|
RHICmdList.SetStreamSource(0, ElementsVertexBuffer, RenderBatch.VertexOffset * sizeof(FSlateVertex));
|
|
|
|
if (RenderBatchOp.InstanceBuffer)
|
|
{
|
|
RHICmdList.SetStreamSource(1, RenderBatchOp.InstanceBuffer, RenderBatch.InstanceOffset * sizeof(FSlateInstanceBufferData::ElementType));
|
|
RHICmdList.DrawIndexedPrimitive(ElementsIndexBuffer, 0, 0, RenderBatch.NumVertices, RenderBatch.IndexOffset, PrimitiveCount, RenderBatch.InstanceCount);
|
|
}
|
|
else
|
|
{
|
|
RHICmdList.SetStreamSource(1, nullptr, 0);
|
|
RHICmdList.DrawIndexedPrimitive(ElementsIndexBuffer, 0, 0, RenderBatch.NumVertices, RenderBatch.IndexOffset, PrimitiveCount, 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (EnumHasAllFlags(RenderBatch.DrawFlags, ESlateBatchDrawFlag::Wireframe) || Inputs.bWireframe)
|
|
{
|
|
State.GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Wireframe>::GetRHI();
|
|
|
|
if (Inputs.bWireframe)
|
|
{
|
|
State.GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
State.GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid>::GetRHI();
|
|
}
|
|
|
|
TShaderMapRef<FSlateElementVS> GlobalVertexShader(Inputs.ShaderMap);
|
|
|
|
State.GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GSlateVertexDeclaration.VertexDeclarationRHI;
|
|
State.GraphicsPSOInit.BoundShaderState.VertexShaderRHI = GlobalVertexShader.GetVertexShader();
|
|
State.GraphicsPSOInit.BoundShaderState.PixelShaderRHI = RenderBatchOp.PixelBindings->Shader.GetPixelShader();
|
|
State.GraphicsPSOInit.PrimitiveType = GetRHIPrimitiveType(RenderBatch.DrawPrimitiveType);
|
|
|
|
#if PSO_PRECACHING_VALIDATE
|
|
if (PSOCollectorStats::IsFullPrecachingValidationEnabled())
|
|
{
|
|
static const int32 GlobalPSOCollectorIndex = FGlobalPSOCollectorManager::GetIndex(SlateGlobalPSOCollectorName);
|
|
PSOCollectorStats::CheckGlobalGraphicsPipelineStateInCache(State.GraphicsPSOInit, GlobalPSOCollectorIndex);
|
|
}
|
|
#endif // PSO_PRECACHING_VALIDATE
|
|
|
|
SetGraphicsPipelineState(RHICmdList, State.GraphicsPSOInit, State.StencilRef);
|
|
|
|
RenderBatchOp.PixelBindings->SetOnCommandList(RHICmdList);
|
|
|
|
RHICmdList.SetStreamSource(0, ElementsVertexBuffer, RenderBatch.VertexOffset * sizeof(FSlateVertex));
|
|
RHICmdList.DrawIndexedPrimitive(ElementsIndexBuffer, 0, 0, RenderBatch.NumVertices, RenderBatch.IndexOffset, PrimitiveCount, RenderBatch.InstanceCount);
|
|
}
|
|
}
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FSlateRenderBatchParameters, )
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FSceneTextureExtractsParameters, SceneTextures)
|
|
RDG_BUFFER_ACCESS(ElementsVertexBuffer, ERHIAccess::VertexOrIndexBuffer)
|
|
RDG_BUFFER_ACCESS(ElementsIndexBuffer, ERHIAccess::VertexOrIndexBuffer)
|
|
SHADER_PARAMETER_STRUCT_REF(FSlateViewUniformParameters, SlateView)
|
|
RENDER_TARGET_BINDING_SLOTS()
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
void AddSlateDrawElementsPass(
|
|
FRDGBuilder& GraphBuilder,
|
|
const FSlateRHIRenderingPolicy& RenderingPolicy,
|
|
const FSlateDrawElementsPassInputs& Inputs,
|
|
TConstArrayView<FSlateRenderBatch> RenderBatches,
|
|
int32 FirstBatchIndex)
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(AddSlateDrawElements);
|
|
|
|
const FIntPoint ElementsTextureExtent = Inputs.ElementsTexture->Desc.Extent;
|
|
const FScreenPassTexture ElementsTexture(Inputs.ElementsTexture);
|
|
|
|
const float EngineGamma = GEngine ? GEngine->GetDisplayGamma() : 2.2f;
|
|
const float DisplayGamma = Inputs.bAllowGammaCorrection && !Inputs.bElementsTextureIsHDRDisplay ? EngineGamma : 1.0f;
|
|
|
|
FGlobalShaderMap* ShaderMap = GetGlobalShaderMap(GMaxRHIShaderPlatform);
|
|
|
|
FSlateRHIResourceManager& ResourceManager = RenderingPolicy.GetResourceManagerRHI();
|
|
|
|
const FSlateSceneViewAllocateInputs SceneViewAllocateInputs =
|
|
{
|
|
.TextureExtent = ElementsTextureExtent
|
|
, .ViewRect = Inputs.SceneViewRect
|
|
, .ViewProjectionMatrix = Inputs.ElementsMatrix
|
|
, .CursorPosition = Inputs.CursorPosition
|
|
, .Time = Inputs.Time
|
|
, .ViewportScaleUI = Inputs.ViewportScaleUI
|
|
};
|
|
|
|
FSlateSceneViewAllocator* SceneViewAllocator = FSlateSceneViewAllocator::Create(GraphBuilder, ResourceManager, SceneViewAllocateInputs);
|
|
|
|
#if WITH_SLATE_VISUALIZERS
|
|
FRandomStream BatchColors(1337);
|
|
#endif
|
|
|
|
const FSlateRenderBatchCreateInputs RenderBatchCreateInputs
|
|
{
|
|
.ShaderMap = ShaderMap
|
|
, .SceneViewAllocator = SceneViewAllocator
|
|
, .TextureLODGroups = GetTextureLODGroups()
|
|
, .DisplayGamma = DisplayGamma
|
|
, .DisplayContrast = GSlateContrast
|
|
, .EngineGamma = EngineGamma
|
|
#if WITH_SLATE_VISUALIZERS
|
|
, .BatchColor = FLinearColor(BatchColors.GetUnitVector())
|
|
#endif
|
|
};
|
|
|
|
// Draw inputs are passed into RDG lambdas and need to be allocated by RDG.
|
|
FSlateRenderBatchDrawInputs* RenderBatchDrawInputs = GraphBuilder.AllocPOD<FSlateRenderBatchDrawInputs>();
|
|
|
|
*RenderBatchDrawInputs =
|
|
{
|
|
.ShaderMap = ShaderMap
|
|
, .ElementsBuffers = Inputs.ElementsBuffers
|
|
, .ElementsViewRect = ElementsTexture.ViewRect
|
|
, .bWireframe = Inputs.bWireframe
|
|
};
|
|
|
|
ERenderTargetLoadAction ElementsLoadAction = Inputs.ElementsLoadAction;
|
|
|
|
const auto ConsumeLoadAction = [] (ERenderTargetLoadAction& InOutLoadAction)
|
|
{
|
|
ERenderTargetLoadAction LoadAction = InOutLoadAction;
|
|
InOutLoadAction = ERenderTargetLoadAction::ELoad;
|
|
return LoadAction;
|
|
};
|
|
|
|
TUniformBufferRef<FSlateViewUniformParameters> SlateViewUniformBuffer;
|
|
|
|
FSlateRenderBatchParameters* NoneStencilActionPassParameters = GraphBuilder.AllocParameters<FSlateRenderBatchParameters>();
|
|
NoneStencilActionPassParameters->SceneTextures = GetSceneTextureExtracts().GetShaderParameters();
|
|
NoneStencilActionPassParameters->ElementsVertexBuffer = Inputs.ElementsBuffers.VertexBuffer;
|
|
NoneStencilActionPassParameters->ElementsIndexBuffer = Inputs.ElementsBuffers.IndexBuffer;
|
|
NoneStencilActionPassParameters->RenderTargets[0] = FRenderTargetBinding(Inputs.ElementsTexture, ERenderTargetLoadAction::ELoad);
|
|
|
|
{
|
|
FSlateViewUniformParameters UniformParameters;
|
|
UniformParameters.ViewProjection = Inputs.ElementsMatrix;
|
|
NoneStencilActionPassParameters->SlateView = TUniformBufferRef<FSlateViewUniformParameters>::CreateUniformBufferImmediate(UniformParameters, UniformBuffer_SingleFrame);
|
|
}
|
|
|
|
FSlateRenderBatchParameters* ClearStencilActionPassParameters = nullptr;
|
|
FSlateRenderBatchParameters* WriteStencilActionPassParameters = nullptr;
|
|
|
|
if (Inputs.StencilTexture)
|
|
{
|
|
WriteStencilActionPassParameters = GraphBuilder.AllocParameters<FSlateRenderBatchParameters>(NoneStencilActionPassParameters);
|
|
WriteStencilActionPassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(Inputs.StencilTexture, ERenderTargetLoadAction::ENoAction, ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthNop_StencilWrite);
|
|
|
|
ClearStencilActionPassParameters = GraphBuilder.AllocParameters<FSlateRenderBatchParameters>(WriteStencilActionPassParameters);
|
|
ClearStencilActionPassParameters->RenderTargets.DepthStencil.SetStencilLoadAction(ERenderTargetLoadAction::EClear);
|
|
}
|
|
|
|
FSlateRenderBatchParameters* LastPassParameters = NoneStencilActionPassParameters;
|
|
const FSlateClippingState* LastClippingState = nullptr;
|
|
const FSlateClippingOp* LastClippingOp = nullptr;
|
|
|
|
FSlateClippingCreateContext ClippingCreateContext;
|
|
|
|
FSlateRenderBatchOp* RenderBatchHeadOp = nullptr;
|
|
FSlateRenderBatchOp* RenderBatchTailOp = nullptr;
|
|
int32 NumRenderBatchOps = 0;
|
|
|
|
const auto FlushDrawElementsPass = [&]
|
|
{
|
|
if (!NumRenderBatchOps)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (ERenderTargetLoadAction LoadAction = ConsumeLoadAction(ElementsLoadAction); LoadAction != ERenderTargetLoadAction::ELoad)
|
|
{
|
|
// Load action differs from the default read one, so make a copy and modify.
|
|
LastPassParameters = GraphBuilder.AllocParameters<FSlateRenderBatchParameters>(LastPassParameters);
|
|
LastPassParameters->RenderTargets[0].SetLoadAction(LoadAction);
|
|
}
|
|
|
|
FRDGPass* Pass = GraphBuilder.AddPass(RDG_EVENT_NAME("ElementBatch"), LastPassParameters, ERDGPassFlags::Raster, [Inputs = RenderBatchDrawInputs, RenderBatchHeadOp] (FRDGAsyncTask, FRHICommandList& RHICmdList)
|
|
{
|
|
RHICmdList.SetViewport(Inputs->ElementsViewRect.Min.X, Inputs->ElementsViewRect.Min.Y, 0.0f, Inputs->ElementsViewRect.Max.X, Inputs->ElementsViewRect.Max.Y, 1.0f);
|
|
RHICmdList.SetScissorRect(false, 0, 0, 0, 0);
|
|
|
|
FSlateRenderBatchDrawState DrawState;
|
|
RHICmdList.ApplyCachedRenderTargets(DrawState.GraphicsPSOInit);
|
|
DrawState.GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
|
|
|
|
const FSlateRenderBatchOp* RenderBatchOp = RenderBatchHeadOp;
|
|
const FSlateRenderBatchOp* LastRenderBatchOp = nullptr;
|
|
|
|
for (; RenderBatchOp != nullptr; RenderBatchOp = RenderBatchOp->Next)
|
|
{
|
|
DrawSlateRenderBatch(RHICmdList, DrawState, *Inputs, *RenderBatchOp);
|
|
LastRenderBatchOp = RenderBatchOp;
|
|
}
|
|
});
|
|
|
|
GraphBuilder.SetPassWorkload(Pass, NumRenderBatchOps);
|
|
RenderBatchHeadOp = RenderBatchTailOp = nullptr;
|
|
NumRenderBatchOps = 0;
|
|
};
|
|
|
|
int32 NextRenderBatchIndex = FirstBatchIndex;
|
|
|
|
while (NextRenderBatchIndex != INDEX_NONE)
|
|
{
|
|
const FSlateRenderBatch& NextRenderBatch = RenderBatches[NextRenderBatchIndex];
|
|
|
|
NextRenderBatchIndex = NextRenderBatch.NextBatchIndex;
|
|
|
|
FSlateRenderBatchParameters* NextPassParameters = LastPassParameters;
|
|
const FSlateClippingState* NextClippingState = NextRenderBatch.ClippingState;
|
|
const FSlateClippingOp* NextClippingOp = LastClippingOp;
|
|
|
|
if (NextClippingState != LastClippingState)
|
|
{
|
|
NextClippingOp = CreateSlateClipping(GraphBuilder, Inputs.ElementsOffset, NextClippingState, ClippingCreateContext);
|
|
|
|
switch (ClippingCreateContext.StencilAction)
|
|
{
|
|
case ESlateClippingStencilAction::Clear:
|
|
NextPassParameters = ClearStencilActionPassParameters;
|
|
break;
|
|
|
|
case ESlateClippingStencilAction::Write:
|
|
NextPassParameters = WriteStencilActionPassParameters;
|
|
break;
|
|
|
|
case ESlateClippingStencilAction::None:
|
|
NextPassParameters = NoneStencilActionPassParameters;
|
|
break;
|
|
}
|
|
|
|
LastClippingState = NextClippingState;
|
|
LastClippingOp = NextClippingOp;
|
|
}
|
|
|
|
const ESlateRenderBatchType NextRenderBatchType = GetSlateRenderBatchType(NextRenderBatch);
|
|
|
|
// Flush all primitive render batches when we encounter one that can't be added.
|
|
if (NextRenderBatchType != ESlateRenderBatchType::Primitive || NextPassParameters != LastPassParameters)
|
|
{
|
|
FlushDrawElementsPass();
|
|
}
|
|
|
|
LastPassParameters = NextPassParameters;
|
|
|
|
switch (NextRenderBatchType)
|
|
{
|
|
case ESlateRenderBatchType::CustomDrawer:
|
|
{
|
|
// Clear the color texture if we haven't done it yet.
|
|
if (ConsumeLoadAction(ElementsLoadAction) == ERenderTargetLoadAction::EClear)
|
|
{
|
|
AddClearRenderTargetPass(GraphBuilder, Inputs.ElementsTexture);
|
|
}
|
|
|
|
ICustomSlateElement::FDrawPassInputs DrawInputs;
|
|
DrawInputs.ElementsMatrix = Inputs.ElementsMatrix;
|
|
DrawInputs.ElementsOffset = Inputs.ElementsOffset;
|
|
DrawInputs.OutputTexture = ElementsTexture.Texture;
|
|
DrawInputs.SceneViewRect = Inputs.SceneViewRect;
|
|
DrawInputs.HDRDisplayColorGamut = Inputs.HDRDisplayColorGamut;
|
|
DrawInputs.UsedSlatePostBuffers = Inputs.UsedSlatePostBuffers;
|
|
DrawInputs.bOutputIsHDRDisplay = Inputs.bElementsTextureIsHDRDisplay;
|
|
DrawInputs.bWireFrame = Inputs.bWireframe;
|
|
|
|
NextRenderBatch.CustomDrawer->Draw_RenderThread(GraphBuilder, DrawInputs);
|
|
|
|
// Reset cached clipping state since custom draws mutate render state.
|
|
LastClippingState = nullptr;
|
|
LastClippingOp = nullptr;
|
|
break;
|
|
}
|
|
case ESlateRenderBatchType::PostProcess:
|
|
{
|
|
const FShaderParams& ShaderParams = NextRenderBatch.ShaderParams;
|
|
|
|
FSlatePostProcessBlurPassInputs BlurInputs;
|
|
|
|
if (Inputs.SceneViewportTexture && Inputs.SceneViewportTexture != Inputs.ElementsTexture)
|
|
{
|
|
// Blur uses the scene viewport texture output as blur input and composites UI separately.
|
|
BlurInputs.InputTexture = Inputs.SceneViewportTexture;
|
|
|
|
if (HasBeenProduced(Inputs.ElementsTexture))
|
|
{
|
|
BlurInputs.SDRCompositeUITexture = Inputs.ElementsTexture;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// UI elements and scene are already composited together.
|
|
BlurInputs.InputTexture = Inputs.ElementsTexture;
|
|
BlurInputs.OutputLoadAction = ConsumeLoadAction(ElementsLoadAction);
|
|
}
|
|
|
|
BlurInputs.InputRect = FIntRect(ShaderParams.PixelParams.X + Inputs.ElementsOffset.X, ShaderParams.PixelParams.Y + Inputs.ElementsOffset.Y, ShaderParams.PixelParams.Z + Inputs.ElementsOffset.X, ShaderParams.PixelParams.W + Inputs.ElementsOffset.Y);
|
|
BlurInputs.OutputTexture = Inputs.SceneViewportTexture ? Inputs.SceneViewportTexture : Inputs.ElementsTexture;
|
|
BlurInputs.OutputRect = BlurInputs.InputRect;
|
|
BlurInputs.ClippingOp = NextClippingOp;
|
|
BlurInputs.ClippingStencilBinding = &NextPassParameters->RenderTargets.DepthStencil;
|
|
BlurInputs.ClippingElementsViewRect = RenderBatchDrawInputs->ElementsViewRect;
|
|
BlurInputs.KernelSize = ShaderParams.PixelParams2.X;
|
|
BlurInputs.Strength = ShaderParams.PixelParams2.Y;
|
|
BlurInputs.DownsampleAmount = ShaderParams.PixelParams2.Z;
|
|
BlurInputs.CornerRadius = ShaderParams.PixelParams3;
|
|
|
|
AddSlatePostProcessBlurPass(GraphBuilder, BlurInputs);
|
|
break;
|
|
}
|
|
case ESlateRenderBatchType::Primitive:
|
|
{
|
|
if (FSlateRenderBatchOp* RenderBatchOp = CreateSlateRenderBatchOp(GraphBuilder, RenderBatchCreateInputs, &NextRenderBatch, NextClippingOp))
|
|
{
|
|
if (!RenderBatchTailOp)
|
|
{
|
|
RenderBatchHeadOp = RenderBatchTailOp = RenderBatchOp;
|
|
}
|
|
else
|
|
{
|
|
RenderBatchTailOp->Next = RenderBatchOp;
|
|
RenderBatchTailOp = RenderBatchOp;
|
|
}
|
|
NumRenderBatchOps++;
|
|
}
|
|
|
|
break;
|
|
}
|
|
default: checkNoEntry();
|
|
}
|
|
}
|
|
|
|
if (NumRenderBatchOps > 0)
|
|
{
|
|
FlushDrawElementsPass();
|
|
}
|
|
|
|
// If no batches were rendered at all, then we might need to just clear the render target.
|
|
if (ConsumeLoadAction(ElementsLoadAction) == ERenderTargetLoadAction::EClear)
|
|
{
|
|
AddClearRenderTargetPass(GraphBuilder, Inputs.ElementsTexture);
|
|
}
|
|
else
|
|
{
|
|
// Don't do color correction on iOS or Android, we don't have the GPU overhead for it.
|
|
#if !(PLATFORM_IOS || PLATFORM_ANDROID)
|
|
if (Inputs.bAllowColorDeficiencyCorrection && GSlateColorDeficiencyType != EColorVisionDeficiency::NormalVision && GSlateColorDeficiencySeverity > 0)
|
|
{
|
|
FSlatePostProcessColorDeficiencyPassInputs ColorDeficiencyInputs;
|
|
ColorDeficiencyInputs.InputTexture = ElementsTexture;
|
|
ColorDeficiencyInputs.OutputTexture = ElementsTexture;
|
|
|
|
AddSlatePostProcessColorDeficiencyPass(GraphBuilder, ColorDeficiencyInputs);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
INC_DWORD_STAT_BY(STAT_SlateScissorClips, ClippingCreateContext.NumScissors);
|
|
INC_DWORD_STAT_BY(STAT_SlateStencilClips, ClippingCreateContext.NumStencils);
|
|
}
|
|
|
|
static TArray<ESlateShader> SlateShaderTypesToPrecache =
|
|
{
|
|
ESlateShader::Default,
|
|
ESlateShader::Border,
|
|
ESlateShader::GrayscaleFont,
|
|
ESlateShader::Custom,
|
|
ESlateShader::RoundedBox
|
|
};
|
|
|
|
void AddSlatePSOInitializer(
|
|
FRHIBlendState* BlendState,
|
|
bool bInstanced,
|
|
ESlateDrawPrimitive DrawPrimitiveType,
|
|
FRHIVertexShader* VertexShaderRHI,
|
|
FRHIPixelShader* PixelShaderRHI,
|
|
const FSceneTexturesConfig& SceneTexturesConfig,
|
|
int32 PSOCollectorIndex,
|
|
TArray<FPSOPrecacheData>& PSOInitializers)
|
|
{
|
|
check(VertexShaderRHI && PixelShaderRHI);
|
|
|
|
FGraphicsPipelineStateInitializer GraphicsPSOInit;
|
|
|
|
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
|
|
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid>::GetRHI();
|
|
GraphicsPSOInit.BlendState = BlendState;
|
|
|
|
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = bInstanced ? GSlateInstancedVertexDeclaration.VertexDeclarationRHI : GSlateVertexDeclaration.VertexDeclarationRHI;
|
|
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShaderRHI;
|
|
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShaderRHI;
|
|
GraphicsPSOInit.PrimitiveType = GetRHIPrimitiveType(DrawPrimitiveType);
|
|
|
|
FGraphicsPipelineRenderTargetsInfo RenderTargetsInfo;
|
|
AddRenderTargetInfo(PF_A2B10G10R10, ETextureCreateFlags::RenderTargetable, RenderTargetsInfo);
|
|
RenderTargetsInfo.NumSamples = 1;
|
|
|
|
GraphicsPSOInit.StatePrecachePSOHash = RHIComputeStatePrecachePSOHash(GraphicsPSOInit);
|
|
ApplyTargetsInfo(GraphicsPSOInit, RenderTargetsInfo);
|
|
|
|
FPSOPrecacheData PSOPrecacheData;
|
|
PSOPrecacheData.bRequired = true;
|
|
PSOPrecacheData.Type = FPSOPrecacheData::EType::Graphics;
|
|
PSOPrecacheData.GraphicsPSOInitializer = GraphicsPSOInit;
|
|
#if PSO_PRECACHING_VALIDATE
|
|
PSOPrecacheData.PSOCollectorIndex = PSOCollectorIndex;
|
|
PSOPrecacheData.VertexFactoryType = nullptr;
|
|
#endif // PSO_PRECACHING_VALIDATE
|
|
|
|
PSOInitializers.Add(MoveTemp(PSOPrecacheData));
|
|
}
|
|
|
|
void SlateGlobalPSOCollector(const FSceneTexturesConfig& SceneTexturesConfig, int32 GlobalPSOCollectorIndex, TArray<FPSOPrecacheData>& PSOInitializers)
|
|
{
|
|
EShaderPlatform ShaderPlatform = SceneTexturesConfig.ShaderPlatform;
|
|
FGlobalShaderMap* GlobalShaderMap = GetGlobalShaderMap(ShaderPlatform);
|
|
|
|
bool bRequired = false;
|
|
|
|
// Collect all possible permutations of the FSlateElementPS (out of 5K possible permutations only 10 unique Pixel shaders are found)
|
|
TSet<FRHIPixelShader*> SlateElementPixelShaders;
|
|
for (int32 ShaderTypeIndex = 0; ShaderTypeIndex <= (int32)ESlateShader::MsdfFont; ++ShaderTypeIndex)
|
|
{
|
|
ESlateShader ShaderType = (ESlateShader) ShaderTypeIndex;
|
|
for (int32 DrawEffectsIndex = 0; DrawEffectsIndex <= (int32)ESlateDrawEffect::ReverseGamma; ++DrawEffectsIndex)
|
|
{
|
|
ESlateDrawEffect DrawEffects = (ESlateDrawEffect) ShaderTypeIndex;
|
|
for (int32 UseTextureGreyScaleIndex = 0; UseTextureGreyScaleIndex < 2; ++UseTextureGreyScaleIndex)
|
|
{
|
|
bool bUseTextureGrayscale = UseTextureGreyScaleIndex > 0;
|
|
for (int32 IsVirtualTextureIndex = 0; IsVirtualTextureIndex < 2; ++IsVirtualTextureIndex)
|
|
{
|
|
bool bIsVirtualTexture = IsVirtualTextureIndex > 0;
|
|
|
|
TShaderRef<FSlateElementPS> SlateElementPS = GetTexturePixelShader(GlobalShaderMap, ShaderType, DrawEffects, bUseTextureGrayscale, bIsVirtualTexture);
|
|
FRHIPixelShader* RHIPixelShader = static_cast<FRHIPixelShader*>(SlateElementPS.GetRHIShaderBase(SF_Pixel, bRequired));
|
|
if (RHIPixelShader)
|
|
{
|
|
SlateElementPixelShaders.Add(RHIPixelShader);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TArray<FBlendStateRHIRef> SlateElementBlendStates;
|
|
SlateElementBlendStates.Add(TStaticBlendState<>::GetRHI());
|
|
SlateElementBlendStates.Add(TStaticBlendState<CW_RGBA, BO_Add, BF_One, BF_InverseSourceAlpha, BO_Add, BF_One, BF_InverseSourceAlpha>::GetRHI());
|
|
SlateElementBlendStates.Add(TStaticBlendState<CW_RGBA, BO_Add, BF_SourceAlpha, BF_InverseSourceAlpha, BO_Add, BF_One, BF_InverseSourceAlpha>::GetRHI());
|
|
|
|
TShaderMapRef<FSlateElementVS> SlateElementVertexShader(GlobalShaderMap);
|
|
FRHIVertexShader* RHIVertexShader = static_cast<FRHIVertexShader*>(SlateElementVertexShader.GetRHIShaderBase(SF_Vertex));
|
|
|
|
for (FRHIPixelShader* SlateElementPixelShader : SlateElementPixelShaders)
|
|
{
|
|
for (FBlendStateRHIRef& BlendState : SlateElementBlendStates)
|
|
{
|
|
bool bInstanced = false;
|
|
AddSlatePSOInitializer(BlendState, bInstanced, ESlateDrawPrimitive::TriangleList, RHIVertexShader, SlateElementPixelShader, SceneTexturesConfig, GlobalPSOCollectorIndex, PSOInitializers);
|
|
}
|
|
}
|
|
}
|
|
|
|
FRegisterGlobalPSOCollectorFunction RegisterSlateGlobalPSOCollector(&SlateGlobalPSOCollector, SlateGlobalPSOCollectorName);
|
|
|
|
class FSlateMaterialPSOCollector : public IPSOCollector
|
|
{
|
|
public:
|
|
FSlateMaterialPSOCollector(ERHIFeatureLevel::Type InFeatureLevel) :
|
|
IPSOCollector(FPSOCollectorCreateManager::GetIndex(GetFeatureLevelShadingPath(InFeatureLevel), SlateMaterialPSOCollectorName)),
|
|
FeatureLevel(InFeatureLevel)
|
|
{
|
|
}
|
|
|
|
virtual void CollectPSOInitializers(
|
|
const FSceneTexturesConfig& SceneTexturesConfig,
|
|
const FMaterial& Material,
|
|
const FPSOPrecacheVertexFactoryData& VertexFactoryData,
|
|
const FPSOPrecacheParams& PreCacheParams,
|
|
TArray<FPSOPrecacheData>& PSOInitializers
|
|
) override final;
|
|
|
|
private:
|
|
|
|
ERHIFeatureLevel::Type FeatureLevel;
|
|
};
|
|
|
|
void FSlateMaterialPSOCollector::CollectPSOInitializers(
|
|
const FSceneTexturesConfig& SceneTexturesConfig,
|
|
const FMaterial& Material,
|
|
const FPSOPrecacheVertexFactoryData& VertexFactoryData,
|
|
const FPSOPrecacheParams& PreCacheParams,
|
|
TArray<FPSOPrecacheData>& PSOInitializers
|
|
)
|
|
{
|
|
if (!Material.IsUIMaterial() || GSlateMaterialPSOPrecache == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
bool bRequired = false;
|
|
for (ESlateShader ShaderType : SlateShaderTypesToPrecache)
|
|
{
|
|
int32 UseInstancingCount = ShaderType == ESlateShader::Custom ? 2 : 1;
|
|
for (int32 UseInstancingIndex = 0; UseInstancingIndex < UseInstancingCount; ++UseInstancingIndex)
|
|
{
|
|
bool bUseInstancing = UseInstancingIndex > 0;
|
|
|
|
FMaterialShaderTypes ShaderTypesToGet;
|
|
if (!ChooseMaterialShaderTypes(ShaderType, bUseInstancing, ShaderTypesToGet))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
FMaterialShaders Shaders;
|
|
if (!Material.TryGetShaders(ShaderTypesToGet, nullptr, Shaders))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
TShaderRef<FSlateMaterialShaderVS> VertexShader;
|
|
TShaderRef<FSlateMaterialShaderPS> PixelShader;
|
|
Shaders.TryGetVertexShader(VertexShader);
|
|
Shaders.TryGetPixelShader(PixelShader);
|
|
if (!VertexShader.IsValid() || !PixelShader.IsValid())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
FRHIVertexShader* RHIVertexShader = VertexShader.GetVertexShader(bRequired);
|
|
FRHIPixelShader* RHIPixelShader = PixelShader.GetPixelShader(bRequired);
|
|
if (RHIVertexShader == nullptr || RHIPixelShader == nullptr)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Don't know if mask resource will be used or not (also precache with blend mode required when mask resource is set?)
|
|
FSlateShaderResource* MaskResource = nullptr;
|
|
FRHIBlendState* BlendState = GetMaterialBlendState(MaskResource, &Material);
|
|
|
|
// Only precache TriangleList
|
|
AddSlatePSOInitializer(BlendState, bUseInstancing, ESlateDrawPrimitive::TriangleList, RHIVertexShader, RHIPixelShader, SceneTexturesConfig, PSOCollectorIndex, PSOInitializers);
|
|
}
|
|
}
|
|
}
|
|
|
|
IPSOCollector* CreateSlateMaterialPSOCollector(ERHIFeatureLevel::Type FeatureLevel)
|
|
{
|
|
return new FSlateMaterialPSOCollector(FeatureLevel);
|
|
}
|
|
FRegisterPSOCollectorCreateFunction RegisterSlateMaterialPSOCollector(&CreateSlateMaterialPSOCollector, EShadingPath::Deferred, SlateMaterialPSOCollectorName);
|
|
|