Files
UnrealEngineUWP/Engine/Source/Runtime/Renderer/Private/SceneHitProxyRendering.cpp
graham wihlidal 25f8e29153 Removed a massive number of Nanite rasterizer shader permutations across all platforms/shaderdbs, significantly improving iteration times for the editor and cooker, especially when these numbers get multiplied by the number of materials that utilize programmable features in addition to the default material "fixed function" path.
Reductions *per material*:

SM5
--
FHWRasterizeVS: 832 -> 21
FHWRasterizePS: 104 -> 39

SM6
--
FHWRasterizeVS: 320 -> 9
FHWRasterizeMS: 640 -> 9
FHWRasterizePS: 120 -> 30

Vulkan
--
FHWRasterizeVS: 320 -> 9
FHWRasterizePS: 40 -> 15

Other platforms redacted =)

-- Details

* CLUSTER_PER_PAGE has been fully removed (since we no longer ever run CLUSTER_PER_PAGE=0), which now makes it mutually inclusive with VIRTUAL_TEXTURE_TARGET
* HAS_RASTER_BIN has been replaced with a dynamic branch, since this is just a per cluster index offset based on a simple uniform buffer load
* ADD_CLUSTER_OFFSET has been replaced with a dynamic branch, since this is just a per cluster index offset based on a simple uniform buffer load
* HAS_PREV_DRAW_DATA has been replaced with a dynamic branch, since this is just a per cluster index offset based on a simple uniform buffer load
* NEAR_CLIP (only change to significantly affect codegen) has been turned into a dynamic branch based on FNaniteView - this lets us merge depth clip/clamp rasterizer calls in VSM together instead of relying on HAS_PREV_DRAW_DATA, and a future optimization can now be done to merge local and directional light full Nanite pipeline calls together.
* VISUALIZE permutation removed from VS/MS since it only loaded unform values that passed down per-vertex into fragment stage as nointerpolation parameters. Pixel shader now constructs this uint2 directly under the VISUALIZE permutation
* NANITE_MESH_SHADER_INTERP removed by default but still left in the code, since it is a work in progress potential optimization for DX12 mesh shaders
* Removed explicit Lumen and VSM usage of NANITE_RENDER_FLAG_HAVE_PREV_DRAW_DATA (now the dynamic branch path is only taken if CullRasterizeMultiPass implicitly breaks the rasterization into multiple calls due to NANITE_MAX_VIEWS_PER_CULL_RASTERIZE_PASS overflow)

Performance was tested on a 2080Ti in AncientGame, and the delta is effectively noise (tested cached and uncached VSM). Further testing on other platforms will occur, but important to get this change in for all the benefits and easy to tweak things later if needed.

#rb rune.stubbe
#fyi brian.karis, ola.olsson, andrew.lauritzen, jamie.hayes, daniel.wright, krzysztof.narkowicz
#preflight 622e684c7e2e35638c96a16a
#robomerge FNNC

[CL 19370372 by graham wihlidal in ue5-main branch]
2022-03-13 23:18:25 -04:00

1178 lines
47 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
SceneHitProxyRendering.cpp: Scene hit proxy rendering.
=============================================================================*/
#include "SceneHitProxyRendering.h"
#include "RendererInterface.h"
#include "BatchedElements.h"
#include "Materials/Material.h"
#include "PostProcess/SceneRenderTargets.h"
#include "MaterialShaderType.h"
#include "MeshMaterialShader.h"
#include "ShaderBaseClasses.h"
#include "SceneRendering.h"
#include "DeferredShadingRenderer.h"
#include "ScenePrivate.h"
#include "DynamicPrimitiveDrawing.h"
#include "ClearQuad.h"
#include "VisualizeTexture.h"
#include "MeshPassProcessor.inl"
#include "GPUScene.h"
#include "Rendering/ColorVertexBuffer.h"
#include "Rendering/NaniteResources.h"
#include "Rendering/NaniteStreamingManager.h"
#include "ShaderPrint.h"
#include "FXSystem.h"
#include "GPUSortManager.h"
#include "VT/VirtualTextureSystem.h"
#include "SceneRenderingUtils.h"
#include "InstanceCulling/InstanceCullingManager.h"
#include "GPUMessaging.h"
#include "HairStrands/HairStrandsData.h"
class FHitProxyShaderElementData : public FMeshMaterialShaderElementData
{
public:
FHitProxyShaderElementData(FHitProxyId InBatchHitProxyId)
: BatchHitProxyId(InBatchHitProxyId)
{
}
FHitProxyId BatchHitProxyId;
};
/**
* A vertex shader for rendering the depth of a mesh.
*/
class FHitProxyVS : public FMeshMaterialShader
{
DECLARE_SHADER_TYPE(FHitProxyVS,MeshMaterial);
public:
static bool ShouldCompilePermutation(const FMeshMaterialShaderPermutationParameters& Parameters)
{
// Only compile the hit proxy vertex shader on desktop editor platforms
return IsPCPlatform(Parameters.Platform) && EnumHasAllFlags(Parameters.Flags, EShaderPermutationFlags::HasEditorOnlyData)
// and only compile for the default material or materials that are masked.
&& (Parameters.MaterialParameters.bIsSpecialEngineMaterial ||
!Parameters.MaterialParameters.bWritesEveryPixel ||
Parameters.MaterialParameters.bMaterialMayModifyMeshPosition ||
Parameters.MaterialParameters.bIsTwoSided);
}
void GetShaderBindings(
const FScene* Scene,
ERHIFeatureLevel::Type FeatureLevel,
const FPrimitiveSceneProxy* PrimitiveSceneProxy,
const FMaterialRenderProxy& MaterialRenderProxy,
const FMaterial& Material,
const FMeshPassProcessorRenderState& DrawRenderState,
const FMeshMaterialShaderElementData& ShaderElementData,
FMeshDrawSingleShaderBindings& ShaderBindings)
{
FMeshMaterialShader::GetShaderBindings(Scene, FeatureLevel, PrimitiveSceneProxy, MaterialRenderProxy, Material, DrawRenderState, ShaderElementData, ShaderBindings);
#if WITH_EDITOR
const FColorVertexBuffer* HitProxyIdBuffer = PrimitiveSceneProxy ? PrimitiveSceneProxy->GetCustomHitProxyIdBuffer() : nullptr;
if(HitProxyIdBuffer)
{
ShaderBindings.Add(VertexFetch_HitProxyIdBuffer, HitProxyIdBuffer->GetColorComponentsSRV());
}
else
{
ShaderBindings.Add(VertexFetch_HitProxyIdBuffer, GNullColorVertexBuffer.VertexBufferSRV);
}
#endif
}
protected:
FHitProxyVS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) :
FMeshMaterialShader(Initializer)
{
VertexFetch_HitProxyIdBuffer.Bind(Initializer.ParameterMap, TEXT("VertexFetch_HitProxyIdBuffer"), SPF_Optional);
}
FHitProxyVS() {}
LAYOUT_FIELD(FShaderResourceParameter, VertexFetch_HitProxyIdBuffer)
};
IMPLEMENT_MATERIAL_SHADER_TYPE(,FHitProxyVS,TEXT("/Engine/Private/HitProxyVertexShader.usf"),TEXT("Main"),SF_Vertex);
/**
* A pixel shader for rendering the HitProxyId of an object as a unique color in the scene.
*/
class FHitProxyPS : public FMeshMaterialShader
{
DECLARE_SHADER_TYPE(FHitProxyPS,MeshMaterial);
public:
static bool ShouldCompilePermutation(const FMeshMaterialShaderPermutationParameters& Parameters)
{
// Only compile the hit proxy vertex shader on desktop editor platforms
return IsPCPlatform(Parameters.Platform) && EnumHasAllFlags(Parameters.Flags, EShaderPermutationFlags::HasEditorOnlyData)
// and only compile for default materials or materials that are masked.
&& (Parameters.MaterialParameters.bIsSpecialEngineMaterial ||
!Parameters.MaterialParameters.bWritesEveryPixel ||
Parameters.MaterialParameters.bMaterialMayModifyMeshPosition ||
Parameters.MaterialParameters.bIsTwoSided);
}
FHitProxyPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer):
FMeshMaterialShader(Initializer)
{
HitProxyId.Bind(Initializer.ParameterMap,TEXT("HitProxyId"), SPF_Optional); // There is no way to guarantee that this parameter will be preserved in a material that kill()s all fragments as the optimiser can remove the global - this happens in various projects.
}
FHitProxyPS() {}
void GetShaderBindings(
const FScene* Scene,
ERHIFeatureLevel::Type FeatureLevel,
const FPrimitiveSceneProxy* PrimitiveSceneProxy,
const FMaterialRenderProxy& MaterialRenderProxy,
const FMaterial& Material,
const FMeshPassProcessorRenderState& DrawRenderState,
const FHitProxyShaderElementData& ShaderElementData,
FMeshDrawSingleShaderBindings& ShaderBindings) const
{
FMeshMaterialShader::GetShaderBindings(Scene, FeatureLevel, PrimitiveSceneProxy, MaterialRenderProxy, Material, DrawRenderState, ShaderElementData, ShaderBindings);
FHitProxyId hitProxyId = ShaderElementData.BatchHitProxyId;
#if WITH_EDITOR
if (PrimitiveSceneProxy && PrimitiveSceneProxy->GetCustomHitProxyIdBuffer())
{
hitProxyId = FColor(0);
}
else
#endif
if (PrimitiveSceneProxy && ShaderElementData.BatchHitProxyId == FHitProxyId())
{
hitProxyId = PrimitiveSceneProxy->GetPrimitiveSceneInfo()->DefaultDynamicHitProxyId;
}
// Per-instance hitproxies are supplied by the vertex factory.
if (PrimitiveSceneProxy && PrimitiveSceneProxy->HasPerInstanceHitProxies())
{
hitProxyId = FColor(0);
}
ShaderBindings.Add(HitProxyId, hitProxyId.GetColor().ReinterpretAsLinear());
}
private:
LAYOUT_FIELD(FShaderParameter, HitProxyId)
};
IMPLEMENT_MATERIAL_SHADER_TYPE(,FHitProxyPS,TEXT("/Engine/Private/HitProxyPixelShader.usf"),TEXT("Main"),SF_Pixel);
#if WITH_EDITOR
void InitHitProxyRender(FRDGBuilder& GraphBuilder, const FSceneRenderer* SceneRenderer, const FSceneTexturesConfig& SceneTexturesConfig, FRDGTextureRef& OutHitProxyTexture, FRDGTextureRef& OutHitProxyDepthTexture)
{
auto& ViewFamily = SceneRenderer->ViewFamily;
auto FeatureLevel = ViewFamily.Scene->GetFeatureLevel();
// Ensure VirtualTexture resources are allocated
if (UseVirtualTexturing(FeatureLevel))
{
FVirtualTextureSystem::Get().AllocateResources(GraphBuilder, FeatureLevel);
FVirtualTextureSystem::Get().CallPendingCallbacks();
// Because there is no Update(), we need to manually finalize the resources
FVirtualTextureSystem::Get().FinalizeResources(GraphBuilder, FeatureLevel);
}
// Initialize global system textures (pass-through if already initialized).
GSystemTextures.InitializeTextures(GraphBuilder.RHICmdList, FeatureLevel);
FRDGSystemTextures::Create(GraphBuilder);
const FMinimalSceneTextures& SceneTextures = FMinimalSceneTextures::Create(GraphBuilder, SceneTexturesConfig);
// Create a texture to store the resolved light attenuation values, and a render-targetable surface to hold the unresolved light attenuation values.
{
FRDGTextureDesc Desc(FRDGTextureDesc::Create2D(SceneTexturesConfig.Extent, PF_B8G8R8A8, FClearValueBinding::Black, TexCreate_RenderTargetable | TexCreate_ShaderResource));
OutHitProxyTexture = GraphBuilder.CreateTexture(Desc, TEXT("HitProxy"));
// create non-MSAA version for hit proxies on PC if needed
const EShaderPlatform CurrentShaderPlatform = GShaderPlatformForFeatureLevel[FeatureLevel];
FRDGTextureDesc DepthDesc = SceneTextures.Depth.Target->Desc;
if (DepthDesc.NumSamples > 1 && RHISupportsSeparateMSAAAndResolveTextures(CurrentShaderPlatform))
{
DepthDesc.NumSamples = 1;
OutHitProxyDepthTexture = GraphBuilder.CreateTexture(DepthDesc, TEXT("NoMSAASceneDepthZ"));
}
else
{
OutHitProxyDepthTexture = SceneTextures.Depth.Target;
}
}
}
BEGIN_SHADER_PARAMETER_STRUCT(FHitProxyPassParameters, )
SHADER_PARAMETER_STRUCT_INCLUDE(FViewShaderParameters, View)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneTextureUniformParameters, SceneTextures)
SHADER_PARAMETER_STRUCT_INCLUDE(FInstanceCullingDrawParams, InstanceCullingDrawParams)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
BEGIN_SHADER_PARAMETER_STRUCT(FHitProxyCopyToViewFamilyParameters, )
RDG_TEXTURE_ACCESS(HitProxyTexture, ERHIAccess::SRVGraphics)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
static void AddViewMeshElementsPass(const TIndirectArray<FMeshBatch> &MeshElements, FRDGBuilder& GraphBuilder, FHitProxyPassParameters* PassParameters, const FScene* Scene, const FViewInfo& View, const FMeshPassProcessorRenderState& DrawRenderState, FInstanceCullingManager& InstanceCullingManager)
{
AddSimpleMeshPass(GraphBuilder, PassParameters, Scene, View, &InstanceCullingManager, RDG_EVENT_NAME("HitProxy::MeshElementsPass"), View.ViewRect,
[&View, Scene, DrawRenderState, &MeshElements](FDynamicPassMeshDrawListContext* DynamicMeshPassContext)
{
FHitProxyMeshProcessor PassMeshProcessor(
Scene,
&View,
View.bAllowTranslucentPrimitivesInHitProxy,
DrawRenderState,
DynamicMeshPassContext);
const uint64 DefaultBatchElementMask = ~0ull;
for (const FMeshBatch& MeshBatch : MeshElements)
{
PassMeshProcessor.AddMeshBatch(MeshBatch, DefaultBatchElementMask, nullptr);
}
}
);
}
static void DoRenderHitProxies(
FRDGBuilder& GraphBuilder,
const FSceneRenderer* SceneRenderer,
FRDGTextureRef HitProxyTexture,
FRDGTextureRef HitProxyDepthTexture,
const TArray<Nanite::FRasterResults, TInlineAllocator<2>>& NaniteRasterResults,
FInstanceCullingManager& InstanceCullingManager)
{
auto& ViewFamily = SceneRenderer->ViewFamily;
auto& Views = SceneRenderer->Views;
const auto FeatureLevel = SceneRenderer->FeatureLevel;
const bool bNeedToSwitchVerticalAxis = RHINeedsToSwitchVerticalAxis(GShaderPlatformForFeatureLevel[FeatureLevel]);
const FIntPoint HitProxyTextureExtent = HitProxyTexture->Desc.Extent;
{
auto* PassParameters = GraphBuilder.AllocParameters<FRenderTargetParameters>();
PassParameters->RenderTargets[0] = FRenderTargetBinding(HitProxyTexture, ERenderTargetLoadAction::EClear);
PassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(HitProxyDepthTexture, ERenderTargetLoadAction::EClear, ERenderTargetLoadAction::EClear, FExclusiveDepthStencil::DepthWrite_StencilWrite);
GraphBuilder.AddPass(
RDG_EVENT_NAME("HitProxies::Clear"),
PassParameters,
ERDGPassFlags::Raster,
[&Views, HitProxyTextureExtent](FRHICommandList& RHICmdList)
{
// Clear color for each view.
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
RHICmdList.SetViewport(View.ViewRect.Min.X, View.ViewRect.Min.Y, 0.0f, View.ViewRect.Max.X, View.ViewRect.Max.Y, 1.0f);
DrawClearQuad(RHICmdList, true, FLinearColor::White, false, 0, false, 0, HitProxyTextureExtent, FIntRect());
// Clear the depth buffer for each DPG.
DrawClearQuad(RHICmdList, false, FLinearColor(), true, (float)ERHIZBuffer::FarPlane, true, 0, HitProxyTextureExtent, FIntRect());
}
});
}
// Nanite hit proxies
if (NaniteRasterResults.Num() == Views.Num())
{
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex)
{
Nanite::DrawHitProxies(GraphBuilder, *SceneRenderer->Scene, Views[ViewIndex], NaniteRasterResults[ViewIndex], HitProxyTexture, HitProxyDepthTexture);
}
}
// HairStrands hit proxies
for (const FViewInfo& View : Views)
{
if (View.HairStrandsMeshElements.Num() > 0)
{
HairStrands::DrawHitProxies(GraphBuilder, *SceneRenderer->Scene, View, InstanceCullingManager, HitProxyTexture, HitProxyDepthTexture);
}
}
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
FViewInfo& View = const_cast<FViewInfo&>(Views[ViewIndex]);
FScene* LocalScene = SceneRenderer->Scene;
View.BeginRenderView();
auto* PassParameters = GraphBuilder.AllocParameters<FHitProxyPassParameters>();
PassParameters->View = View.GetShaderParameters();
// Adjust the visibility map for this view
if (View.bAllowTranslucentPrimitivesInHitProxy)
{
View.ParallelMeshDrawCommandPasses[EMeshPass::HitProxy].BuildRenderingCommands(GraphBuilder, LocalScene->GPUScene, PassParameters->InstanceCullingDrawParams);
}
else
{
View.ParallelMeshDrawCommandPasses[EMeshPass::HitProxyOpaqueOnly].BuildRenderingCommands(GraphBuilder, LocalScene->GPUScene, PassParameters->InstanceCullingDrawParams);
}
PassParameters->RenderTargets[0] = FRenderTargetBinding(HitProxyTexture, ERenderTargetLoadAction::ELoad);
PassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(HitProxyDepthTexture, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthWrite_StencilWrite);
PassParameters->SceneTextures = CreateSceneTextureUniformBuffer(GraphBuilder, SceneRenderer->FeatureLevel, ESceneTextureSetupMode::None);
GraphBuilder.AddPass(
RDG_EVENT_NAME("HitProxies::Render"),
PassParameters,
ERDGPassFlags::Raster,
[SceneRenderer, &View, LocalScene, FeatureLevel, bNeedToSwitchVerticalAxis, PassParameters](FRHICommandListImmediate& RHICmdList)
{
FMeshPassProcessorRenderState DrawRenderState;
// Set the device viewport for the view.
RHICmdList.SetViewport(View.ViewRect.Min.X, View.ViewRect.Min.Y, 0.0f, View.ViewRect.Max.X, View.ViewRect.Max.Y, 1.0f);
// Depth tests + writes, no alpha blending.
DrawRenderState.SetDepthStencilState(TStaticDepthStencilState<true, CF_DepthNearOrEqual>::GetRHI());
DrawRenderState.SetBlendState(TStaticBlendState<>::GetRHI());
const bool bHitTesting = true;
// Adjust the visibility map for this view
if (View.bAllowTranslucentPrimitivesInHitProxy)
{
View.ParallelMeshDrawCommandPasses[EMeshPass::HitProxy].DispatchDraw(nullptr, RHICmdList, &PassParameters->InstanceCullingDrawParams);
}
else
{
View.ParallelMeshDrawCommandPasses[EMeshPass::HitProxyOpaqueOnly].DispatchDraw(nullptr, RHICmdList, &PassParameters->InstanceCullingDrawParams);
}
DrawDynamicMeshPass(View, RHICmdList,
[&View, &DrawRenderState, LocalScene](FDynamicPassMeshDrawListContext* DynamicMeshPassContext)
{
FHitProxyMeshProcessor PassMeshProcessor(
LocalScene,
&View,
View.bAllowTranslucentPrimitivesInHitProxy,
DrawRenderState,
DynamicMeshPassContext);
const uint64 DefaultBatchElementMask = ~0ull;
for (int32 MeshIndex = 0; MeshIndex < View.DynamicEditorMeshElements.Num(); MeshIndex++)
{
const FMeshBatchAndRelevance& MeshBatchAndRelevance = View.DynamicEditorMeshElements[MeshIndex];
PassMeshProcessor.AddMeshBatch(*MeshBatchAndRelevance.Mesh, DefaultBatchElementMask, MeshBatchAndRelevance.PrimitiveSceneProxy);
}
});
View.SimpleElementCollector.DrawBatchedElements(RHICmdList, DrawRenderState, View, EBlendModeFilter::All, SDPG_World);
View.SimpleElementCollector.DrawBatchedElements(RHICmdList, DrawRenderState, View, EBlendModeFilter::All, SDPG_Foreground);
View.EditorSimpleElementCollector.DrawBatchedElements(RHICmdList, DrawRenderState, View, EBlendModeFilter::All, SDPG_World);
View.EditorSimpleElementCollector.DrawBatchedElements(RHICmdList, DrawRenderState, View, EBlendModeFilter::All, SDPG_Foreground);
DrawDynamicMeshPass(View, RHICmdList,
[&View, &DrawRenderState, LocalScene](FDynamicPassMeshDrawListContext* DynamicMeshPassContext)
{
FHitProxyMeshProcessor PassMeshProcessor(
LocalScene,
&View,
View.bAllowTranslucentPrimitivesInHitProxy,
DrawRenderState,
DynamicMeshPassContext);
const uint64 DefaultBatchElementMask = ~0ull;
for (int32 MeshIndex = 0; MeshIndex < View.ViewMeshElements.Num(); MeshIndex++)
{
const FMeshBatch& MeshBatch = View.ViewMeshElements[MeshIndex];
PassMeshProcessor.AddMeshBatch(MeshBatch, DefaultBatchElementMask, nullptr);
}
});
DrawDynamicMeshPass(View, RHICmdList,
[&View, &DrawRenderState, LocalScene](FDynamicPassMeshDrawListContext* DynamicMeshPassContext)
{
FHitProxyMeshProcessor PassMeshProcessor(
LocalScene,
&View,
View.bAllowTranslucentPrimitivesInHitProxy,
DrawRenderState,
DynamicMeshPassContext);
const uint64 DefaultBatchElementMask = ~0ull;
for (int32 MeshIndex = 0; MeshIndex < View.TopViewMeshElements.Num(); MeshIndex++)
{
const FMeshBatch& MeshBatch = View.TopViewMeshElements[MeshIndex];
PassMeshProcessor.AddMeshBatch(MeshBatch, DefaultBatchElementMask, nullptr);
}
});
// Draw the view's batched simple elements(lines, sprites, etc).
View.BatchedViewElements.Draw(RHICmdList, DrawRenderState, FeatureLevel, bNeedToSwitchVerticalAxis, View, true);
// Some elements should never be occluded (e.g. gizmos).
// So we render those twice, first to overwrite potentially nearer objects,
// then again to allows proper occlusion within those elements.
DrawRenderState.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always>::GetRHI());
DrawDynamicMeshPass(View, RHICmdList,
[&View, &DrawRenderState, LocalScene](FDynamicPassMeshDrawListContext* DynamicMeshPassContext)
{
FHitProxyMeshProcessor PassMeshProcessor(
LocalScene,
&View,
View.bAllowTranslucentPrimitivesInHitProxy,
DrawRenderState,
DynamicMeshPassContext);
const uint64 DefaultBatchElementMask = ~0ull;
for (int32 MeshIndex = 0; MeshIndex < View.TopViewMeshElements.Num(); MeshIndex++)
{
const FMeshBatch& MeshBatch = View.TopViewMeshElements[MeshIndex];
PassMeshProcessor.AddMeshBatch(MeshBatch, DefaultBatchElementMask, nullptr);
}
});
View.TopBatchedViewElements.Draw(RHICmdList, DrawRenderState, FeatureLevel, bNeedToSwitchVerticalAxis, View, true);
DrawRenderState.SetDepthStencilState(TStaticDepthStencilState<true, CF_DepthNearOrEqual>::GetRHI());
DrawDynamicMeshPass(View, RHICmdList,
[&View, &DrawRenderState, LocalScene](FDynamicPassMeshDrawListContext* DynamicMeshPassContext)
{
FHitProxyMeshProcessor PassMeshProcessor(
LocalScene,
&View,
View.bAllowTranslucentPrimitivesInHitProxy,
DrawRenderState,
DynamicMeshPassContext);
const uint64 DefaultBatchElementMask = ~0ull;
for (int32 MeshIndex = 0; MeshIndex < View.TopViewMeshElements.Num(); MeshIndex++)
{
const FMeshBatch& MeshBatch = View.TopViewMeshElements[MeshIndex];
PassMeshProcessor.AddMeshBatch(MeshBatch, DefaultBatchElementMask, nullptr);
}
});
View.TopBatchedViewElements.Draw(RHICmdList, DrawRenderState, FeatureLevel, bNeedToSwitchVerticalAxis, View, true);
});
}
FRDGTextureRef ViewFamilyTexture = TryCreateViewFamilyTexture(GraphBuilder, ViewFamily);
check(ViewFamilyTexture);
//
// Copy the hit proxy buffer into the view family's render target.
//
{
auto* PassParameters = GraphBuilder.AllocParameters<FHitProxyCopyToViewFamilyParameters>();
PassParameters->HitProxyTexture = HitProxyTexture;
PassParameters->RenderTargets[0] = FRenderTargetBinding(ViewFamilyTexture, ERenderTargetLoadAction::ELoad);
GraphBuilder.AddPass(
RDG_EVENT_NAME("HitProxies::CopyOutput"),
PassParameters,
ERDGPassFlags::Raster,
[&Views, HitProxyTextureExtent, HitProxyTexture, ViewFamilyTexture, FeatureLevel, bNeedToSwitchVerticalAxis](FRHICommandListImmediate& RHICmdList)
{
// Set up a FTexture that is used to draw the hit proxy buffer to the view family's render target.
FTexture HitProxyRenderTargetTexture;
HitProxyRenderTargetTexture.TextureRHI = HitProxyTexture->GetRHI();
HitProxyRenderTargetTexture.SamplerStateRHI = TStaticSamplerState<>::GetRHI();
// Generate the vertices and triangles mapping the hit proxy RT pixels into the view family's RT pixels.
FBatchedElements BatchedElements;
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
float InvBufferSizeX = 1.0f / HitProxyTextureExtent.X;
float InvBufferSizeY = 1.0f / HitProxyTextureExtent.Y;
const float U0 = View.ViewRect.Min.X * InvBufferSizeX;
const float V0 = View.ViewRect.Min.Y * InvBufferSizeY;
const float U1 = View.ViewRect.Max.X * InvBufferSizeX;
const float V1 = View.ViewRect.Max.Y * InvBufferSizeY;
// Note: High DPI . We are drawing to the size of the unscaled view rect because that is the size of the views render target
// if we do not do this clicking would be off.
const int32 V00 = BatchedElements.AddVertex(FVector4(View.UnscaledViewRect.Min.X, View.UnscaledViewRect.Min.Y, 0, 1), FVector2D(U0, V0), FLinearColor::White, FHitProxyId());
const int32 V10 = BatchedElements.AddVertex(FVector4(View.UnscaledViewRect.Max.X, View.UnscaledViewRect.Min.Y, 0, 1), FVector2D(U1, V0), FLinearColor::White, FHitProxyId());
const int32 V01 = BatchedElements.AddVertex(FVector4(View.UnscaledViewRect.Min.X, View.UnscaledViewRect.Max.Y, 0, 1), FVector2D(U0, V1), FLinearColor::White, FHitProxyId());
const int32 V11 = BatchedElements.AddVertex(FVector4(View.UnscaledViewRect.Max.X, View.UnscaledViewRect.Max.Y, 0, 1), FVector2D(U1, V1), FLinearColor::White, FHitProxyId());
BatchedElements.AddTriangle(V00, V10, V11, &HitProxyRenderTargetTexture, BLEND_Opaque);
BatchedElements.AddTriangle(V00, V11, V01, &HitProxyRenderTargetTexture, BLEND_Opaque);
}
// Generate a transform which maps from view family RT pixel coordinates to Normalized Device Coordinates.
FIntPoint ViewFamilyTextureExtent = ViewFamilyTexture->Desc.Extent;
const FMatrix PixelToView =
FTranslationMatrix(FVector(0, 0, 0)) *
FMatrix(
FPlane(1.0f / ((float)ViewFamilyTextureExtent.X / 2.0f), 0.0, 0.0f, 0.0f),
FPlane(0.0f, -GProjectionSignY / ((float)ViewFamilyTextureExtent.Y / 2.0f), 0.0f, 0.0f),
FPlane(0.0f, 0.0f, 1.0f, 0.0f),
FPlane(-1.0f, GProjectionSignY, 0.0f, 1.0f)
);
FSceneView SceneView = FBatchedElements::CreateProxySceneView(PixelToView, FIntRect(0, 0, ViewFamilyTextureExtent.X, ViewFamilyTextureExtent.Y));
FMeshPassProcessorRenderState DrawRenderState;
DrawRenderState.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always>::GetRHI());
DrawRenderState.SetBlendState(TStaticBlendState<>::GetRHI());
BatchedElements.Draw(
RHICmdList,
DrawRenderState,
FeatureLevel,
bNeedToSwitchVerticalAxis,
SceneView,
false,
1.0f
);
RHICmdList.EndScene();
});
}
}
#endif
void FMobileSceneRenderer::RenderHitProxies(FRDGBuilder& GraphBuilder)
{
Scene->UpdateAllPrimitiveSceneInfos(GraphBuilder);
GPU_MESSAGE_SCOPE(GraphBuilder);
FGPUSceneScopeBeginEndHelper GPUSceneScopeBeginEndHelper(Scene->GPUScene, GPUSceneDynamicContext, Scene);
PrepareViewRectsForRendering(GraphBuilder.RHICmdList);
#if WITH_EDITOR
FSceneTexturesConfig SceneTexturesConfig = FSceneTexturesConfig::Create(ViewFamily);
FRDGTextureRef HitProxyTexture = nullptr;
FRDGTextureRef HitProxyDepthTexture = nullptr;
InitHitProxyRender(GraphBuilder, this, SceneTexturesConfig, HitProxyTexture, HitProxyDepthTexture);
FInstanceCullingManager& InstanceCullingManager = *GraphBuilder.AllocObject<FInstanceCullingManager>(Scene->GPUScene.IsEnabled(), GraphBuilder);
// Find the visible primitives.
InitViews(GraphBuilder, SceneTexturesConfig, InstanceCullingManager);
GEngine->GetPreRenderDelegateEx().Broadcast(GraphBuilder);
// Global dynamic buffers need to be committed before rendering.
DynamicIndexBuffer.Commit();
DynamicVertexBuffer.Commit();
DynamicReadBuffer.Commit();
InstanceCullingManager.FlushRegisteredViews(GraphBuilder);
TArray<Nanite::FRasterResults, TInlineAllocator<2>> NaniteRasterResults;
::DoRenderHitProxies(GraphBuilder, this, HitProxyTexture, HitProxyDepthTexture, NaniteRasterResults, InstanceCullingManager);
GEngine->GetPostRenderDelegateEx().Broadcast(GraphBuilder);
#endif
}
void FDeferredShadingSceneRenderer::RenderHitProxies(FRDGBuilder& GraphBuilder)
{
static const bool bNaniteEnabled = UseNanite(ShaderPlatform);
Scene->UpdateAllPrimitiveSceneInfos(GraphBuilder);
GPU_MESSAGE_SCOPE(GraphBuilder);
FGPUSceneScopeBeginEndHelper GPUSceneScopeBeginEndHelper(Scene->GPUScene, GPUSceneDynamicContext, Scene);
PrepareViewRectsForRendering(GraphBuilder.RHICmdList);
#if WITH_EDITOR
const FSceneTexturesConfig SceneTexturesConfig = FSceneTexturesConfig::Create(ViewFamily);
FSceneTexturesConfig::Set(SceneTexturesConfig);
FRDGTextureRef HitProxyTexture = nullptr;
FRDGTextureRef HitProxyDepthTexture = nullptr;
InitHitProxyRender(GraphBuilder, this, SceneTexturesConfig, HitProxyTexture, HitProxyDepthTexture);
const FIntPoint HitProxyTextureSize = HitProxyDepthTexture->Desc.Extent;
FInstanceCullingManager& InstanceCullingManager = *GraphBuilder.AllocObject<FInstanceCullingManager>(Scene->GPUScene.IsEnabled(), GraphBuilder);
// Find the visible primitives.
{
FILCUpdatePrimTaskData ILCTaskData;
InitViews(GraphBuilder, SceneTexturesConfig, FExclusiveDepthStencil::DepthWrite_StencilWrite, ILCTaskData, InstanceCullingManager);
InitViewsAfterPrepass(GraphBuilder, ILCTaskData, InstanceCullingManager);
}
extern TSet<IPersistentViewUniformBufferExtension*> PersistentViewUniformBufferExtensions;
for (IPersistentViewUniformBufferExtension* Extension : PersistentViewUniformBufferExtensions)
{
Extension->BeginFrame();
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
// Must happen before RHI thread flush so any tasks we dispatch here can land in the idle gap during the flush
Extension->PrepareView(&Views[ViewIndex]);
}
}
Scene->GPUScene.Update(GraphBuilder, *Scene);
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
ShaderPrint::BeginView(GraphBuilder, Views[ViewIndex]);
}
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
Scene->GPUScene.UploadDynamicPrimitiveShaderDataForView(GraphBuilder, Scene, Views[ViewIndex]);
}
InstanceCullingManager.FlushRegisteredViews(GraphBuilder);
if (bNaniteEnabled)
{
Nanite::GGlobalResources.Update(GraphBuilder);
Nanite::GStreamingManager.BeginAsyncUpdate(GraphBuilder);
Nanite::GStreamingManager.EndAsyncUpdate(GraphBuilder);
}
GEngine->GetPreRenderDelegateEx().Broadcast(GraphBuilder);
// Global dynamic buffers need to be committed before rendering.
DynamicIndexBufferForInitViews.Commit();
DynamicVertexBufferForInitViews.Commit();
DynamicReadBufferForInitViews.Commit();
// Notify the FX system that the scene is about to be rendered.
if (FXSystem && Views.IsValidIndex(0))
{
FGPUSortManager* GPUSortManager = FXSystem->GetGPUSortManager();
FXSystem->PreRender(GraphBuilder, Views, false);
if (GPUSortManager)
{
GPUSortManager->OnPreRender(GraphBuilder);
}
// Call PostRenderOpaque now as this is irrelevant for when rendering hit proxies.
// because we don't tick the particles in the render loop (see last param being "false").
FXSystem->PostRenderOpaque(GraphBuilder, Views, false /*bAllowGPUParticleUpdate*/);
if (GPUSortManager)
{
GPUSortManager->OnPostRenderOpaque(GraphBuilder);
}
}
TArray<Nanite::FRasterResults, TInlineAllocator<2>> NaniteRasterResults;
if (bNaniteEnabled)
{
NaniteRasterResults.AddDefaulted(Views.Num());
Nanite::FSharedContext SharedContext{};
SharedContext.FeatureLevel = Scene->GetFeatureLevel();
SharedContext.ShaderMap = GetGlobalShaderMap(SharedContext.FeatureLevel);
SharedContext.Pipeline = Nanite::EPipeline::HitProxy;
Nanite::FRasterState RasterState;
Nanite::FRasterContext RasterContext = Nanite::InitRasterContext(GraphBuilder, SharedContext, HitProxyTextureSize, false);
Nanite::FCullingContext::FConfiguration CullingConfig = {0};
CullingConfig.bForceHWRaster = RasterContext.RasterScheduling == Nanite::ERasterScheduling::HardwareOnly;
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
CullingConfig.SetViewFlags(View);
Nanite::FCullingContext CullingContext = Nanite::InitCullingContext(
GraphBuilder,
SharedContext,
*Scene,
nullptr,
FIntRect(),
CullingConfig
);
Nanite::FPackedView PackedView = Nanite::CreatePackedViewFromViewInfo(View, HitProxyTextureSize, NANITE_VIEW_FLAG_HZBTEST | NANITE_VIEW_FLAG_NEAR_CLIP);
Nanite::CullRasterize(GraphBuilder, Scene->NaniteRasterPipelines[ENaniteMeshPass::BasePass], *Scene, View, { PackedView }, SharedContext, CullingContext, RasterContext, RasterState);
Nanite::ExtractResults(GraphBuilder, CullingContext, RasterContext, NaniteRasterResults[ViewIndex]);
}
}
::DoRenderHitProxies(GraphBuilder, this, HitProxyTexture, HitProxyDepthTexture, NaniteRasterResults, InstanceCullingManager);
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
ShaderPrint::EndView(Views[ViewIndex]);
}
GEngine->GetPostRenderDelegateEx().Broadcast(GraphBuilder);
#endif
}
#if WITH_EDITOR
bool FHitProxyMeshProcessor::TryAddMeshBatch(const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, int32 StaticMeshId, const FMaterialRenderProxy* MaterialRenderProxy, const FMaterial* Material)
{
const EBlendMode BlendMode = Material->GetBlendMode();
const FMeshDrawingPolicyOverrideSettings OverrideSettings = ComputeMeshOverrideSettings(MeshBatch);
const ERasterizerFillMode MeshFillMode = ComputeMeshFillMode(MeshBatch, *Material, OverrideSettings);
const ERasterizerCullMode MeshCullMode = ComputeMeshCullMode(MeshBatch, *Material, OverrideSettings);
if (Material->WritesEveryPixel() && !Material->IsTwoSided() && !Material->MaterialModifiesMeshPosition_RenderThread())
{
// Default material doesn't handle masked, and doesn't have the correct bIsTwoSided setting.
MaterialRenderProxy = UMaterial::GetDefaultMaterial(MD_Surface)->GetRenderProxy();
check(MaterialRenderProxy);
Material = MaterialRenderProxy->GetMaterialNoFallback(FeatureLevel);
}
check(Material && MaterialRenderProxy);
bool bAddTranslucentPrimitive = bAllowTranslucentPrimitivesInHitProxy;
// Check whether the primitive overrides the pass to force translucent hit proxies.
if (!bAddTranslucentPrimitive)
{
FHitProxyId HitProxyId = MeshBatch.BatchHitProxyId;
// Fallback to the primitive default hit proxy id if the mesh batch doesn't have one.
if (MeshBatch.BatchHitProxyId == FHitProxyId() && PrimitiveSceneProxy)
{
if (const FPrimitiveSceneInfo* PrimitiveSceneInfo = PrimitiveSceneProxy->GetPrimitiveSceneInfo())
{
HitProxyId = PrimitiveSceneInfo->DefaultDynamicHitProxyId;
}
}
if (const HHitProxy* HitProxy = GetHitProxyById(HitProxyId))
{
bAddTranslucentPrimitive = HitProxy->AlwaysAllowsTranslucentPrimitives();
}
}
bool bResult = true;
if (bAddTranslucentPrimitive || !IsTranslucentBlendMode(BlendMode))
{
bResult = Process(MeshBatch, BatchElementMask, StaticMeshId, PrimitiveSceneProxy, *MaterialRenderProxy, *Material, MeshFillMode, MeshCullMode);
}
return bResult;
}
void FHitProxyMeshProcessor::AddMeshBatch(const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, int32 StaticMeshId)
{
if (MeshBatch.BatchHitProxyId == FHitProxyId::InvisibleHitProxyId)
{
return;
}
if (MeshBatch.bUseForMaterial && MeshBatch.bSelectable && Scene->RequiresHitProxies() && (!PrimitiveSceneProxy || PrimitiveSceneProxy->IsSelectable()))
{
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);
}
}
}
bool GetHitProxyPassShaders(
const FMaterial& Material,
FVertexFactoryType* VertexFactoryType,
ERHIFeatureLevel::Type FeatureLevel,
TShaderRef<FHitProxyVS>& VertexShader,
TShaderRef<FHitProxyPS>& PixelShader)
{
FMaterialShaderTypes ShaderTypes;
ShaderTypes.AddShaderType<FHitProxyVS>();
ShaderTypes.AddShaderType<FHitProxyPS>();
FMaterialShaders Shaders;
if (!Material.TryGetShaders(ShaderTypes, VertexFactoryType, Shaders))
{
return false;
}
Shaders.TryGetVertexShader(VertexShader);
Shaders.TryGetPixelShader(PixelShader);
return true;
}
bool FHitProxyMeshProcessor::Process(
const FMeshBatch& MeshBatch,
uint64 BatchElementMask,
int32 StaticMeshId,
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
const FMaterialRenderProxy& RESTRICT MaterialRenderProxy,
const FMaterial& RESTRICT MaterialResource,
ERasterizerFillMode MeshFillMode,
ERasterizerCullMode MeshCullMode)
{
const FVertexFactory* VertexFactory = MeshBatch.VertexFactory;
TMeshProcessorShaders<
FHitProxyVS,
FHitProxyPS> HitProxyPassShaders;
if (!GetHitProxyPassShaders(
MaterialResource,
VertexFactory->GetType(),
FeatureLevel,
HitProxyPassShaders.VertexShader,
HitProxyPassShaders.PixelShader))
{
return false;
}
FHitProxyShaderElementData ShaderElementData(MeshBatch.BatchHitProxyId);
ShaderElementData.InitializeMeshMaterialData(ViewIfDynamicMeshCommand, PrimitiveSceneProxy, MeshBatch, StaticMeshId, false);
const FMeshDrawCommandSortKey SortKey = CalculateMeshStaticSortKey(HitProxyPassShaders.VertexShader, HitProxyPassShaders.PixelShader);
BuildMeshDrawCommands(
MeshBatch,
BatchElementMask,
PrimitiveSceneProxy,
MaterialRenderProxy,
MaterialResource,
PassDrawRenderState,
HitProxyPassShaders,
MeshFillMode,
MeshCullMode,
SortKey,
EMeshPassFeatures::Default,
ShaderElementData);
return true;
}
FHitProxyMeshProcessor::FHitProxyMeshProcessor(const FScene* Scene, const FSceneView* InViewIfDynamicMeshCommand, bool InbAllowTranslucentPrimitivesInHitProxy, const FMeshPassProcessorRenderState& InRenderState, FMeshPassDrawListContext* InDrawListContext)
: FMeshPassProcessor(Scene, Scene->GetFeatureLevel(), InViewIfDynamicMeshCommand, InDrawListContext)
, PassDrawRenderState(InRenderState)
, bAllowTranslucentPrimitivesInHitProxy(InbAllowTranslucentPrimitivesInHitProxy)
{
}
FMeshPassProcessor* CreateHitProxyPassProcessor(const FScene* Scene, const FSceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext)
{
FMeshPassProcessorRenderState PassDrawRenderState;
PassDrawRenderState.SetDepthStencilState(TStaticDepthStencilState<true, CF_DepthNearOrEqual>::GetRHI());
PassDrawRenderState.SetBlendState(TStaticBlendState<>::GetRHI());
return new(FMemStack::Get()) FHitProxyMeshProcessor(Scene, InViewIfDynamicMeshCommand, true, PassDrawRenderState, InDrawListContext);
}
FMeshPassProcessor* CreateHitProxyOpaqueOnlyPassProcessor(const FScene* Scene, const FSceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext)
{
FMeshPassProcessorRenderState PassDrawRenderState;
PassDrawRenderState.SetDepthStencilState(TStaticDepthStencilState<true, CF_DepthNearOrEqual>::GetRHI());
PassDrawRenderState.SetBlendState(TStaticBlendState<>::GetRHI());
return new(FMemStack::Get()) FHitProxyMeshProcessor(Scene, InViewIfDynamicMeshCommand, false, PassDrawRenderState, InDrawListContext);
}
FRegisterPassProcessorCreateFunction RegisterHitProxyPass(&CreateHitProxyPassProcessor, EShadingPath::Deferred, EMeshPass::HitProxy, EMeshPassFlags::CachedMeshCommands | EMeshPassFlags::MainView);
FRegisterPassProcessorCreateFunction RegisterHitProxyOpaqueOnlyPass(&CreateHitProxyOpaqueOnlyPassProcessor, EShadingPath::Deferred, EMeshPass::HitProxyOpaqueOnly, EMeshPassFlags::CachedMeshCommands | EMeshPassFlags::MainView);
FRegisterPassProcessorCreateFunction RegisterMobileHitProxyPass(&CreateHitProxyPassProcessor, EShadingPath::Mobile, EMeshPass::HitProxy, EMeshPassFlags::CachedMeshCommands | EMeshPassFlags::MainView);
FRegisterPassProcessorCreateFunction RegisterMobileHitProxyOpaqueOnlyPass(&CreateHitProxyOpaqueOnlyPassProcessor, EShadingPath::Mobile, EMeshPass::HitProxyOpaqueOnly, EMeshPassFlags::CachedMeshCommands | EMeshPassFlags::MainView);
bool FEditorSelectionMeshProcessor::TryAddMeshBatch(const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, int32 StaticMeshId, const FMaterialRenderProxy* MaterialRenderProxy, const FMaterial* Material)
{
const FMeshDrawingPolicyOverrideSettings OverrideSettings = ComputeMeshOverrideSettings(MeshBatch);
const ERasterizerFillMode MeshFillMode = ComputeMeshFillMode(MeshBatch, *Material, OverrideSettings);
const ERasterizerCullMode MeshCullMode = CM_None;
if (Material->WritesEveryPixel() && !Material->IsTwoSided() && !Material->MaterialModifiesMeshPosition_RenderThread())
{
// Default material doesn't handle masked, and doesn't have the correct bIsTwoSided setting.
MaterialRenderProxy = UMaterial::GetDefaultMaterial(MD_Surface)->GetRenderProxy();
check(MaterialRenderProxy);
Material = MaterialRenderProxy->GetMaterialNoFallback(FeatureLevel);
}
check(Material && MaterialRenderProxy);
return Process(MeshBatch, BatchElementMask, StaticMeshId, PrimitiveSceneProxy, *MaterialRenderProxy, *Material, MeshFillMode, MeshCullMode);
}
void FEditorSelectionMeshProcessor::AddMeshBatch(const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, int32 StaticMeshId)
{
if (MeshBatch.bUseForMaterial
&& MeshBatch.bUseSelectionOutline
&& PrimitiveSceneProxy
&& PrimitiveSceneProxy->WantsSelectionOutline()
&& (PrimitiveSceneProxy->IsSelected() || PrimitiveSceneProxy->IsHovered()))
{
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);
}
}
}
bool FEditorSelectionMeshProcessor::Process(
const FMeshBatch& MeshBatch,
uint64 BatchElementMask,
int32 StaticMeshId,
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
const FMaterialRenderProxy& RESTRICT MaterialRenderProxy,
const FMaterial& RESTRICT MaterialResource,
ERasterizerFillMode MeshFillMode,
ERasterizerCullMode MeshCullMode)
{
const FVertexFactory* VertexFactory = MeshBatch.VertexFactory;
TMeshProcessorShaders<
FHitProxyVS,
FHitProxyPS> HitProxyPassShaders;
if (!GetHitProxyPassShaders(
MaterialResource,
VertexFactory->GetType(),
FeatureLevel,
HitProxyPassShaders.VertexShader,
HitProxyPassShaders.PixelShader))
{
return false;
}
const int32 StencilRef = GetStencilValue(ViewIfDynamicMeshCommand, PrimitiveSceneProxy);
PassDrawRenderState.SetStencilRef(StencilRef);
FHitProxyId DummyId;
FHitProxyShaderElementData ShaderElementData(DummyId);
ShaderElementData.InitializeMeshMaterialData(ViewIfDynamicMeshCommand, PrimitiveSceneProxy, MeshBatch, StaticMeshId, false);
const FMeshDrawCommandSortKey SortKey = CalculateMeshStaticSortKey(HitProxyPassShaders.VertexShader, HitProxyPassShaders.PixelShader);
BuildMeshDrawCommands(
MeshBatch,
BatchElementMask,
PrimitiveSceneProxy,
MaterialRenderProxy,
MaterialResource,
PassDrawRenderState,
HitProxyPassShaders,
MeshFillMode,
MeshCullMode,
SortKey,
EMeshPassFeatures::Default,
ShaderElementData);
return true;
}
int32 FEditorSelectionMeshProcessor::GetStencilValue(const FSceneView* View, const FPrimitiveSceneProxy* PrimitiveSceneProxy)
{
const bool bActorSelectionColorIsSubdued = View->bHasSelectedComponents;
const int32* ExistingStencilValue = PrimitiveSceneProxy->IsIndividuallySelected() ? ProxyToStencilIndex.Find(PrimitiveSceneProxy) : ActorNameToStencilIndex.Find(PrimitiveSceneProxy->GetOwnerName());
int32 StencilValue = 0;
if (PrimitiveSceneProxy->GetOwnerName() == NAME_BSP)
{
StencilValue = 1;
}
else if (ExistingStencilValue != nullptr)
{
StencilValue = *ExistingStencilValue;
}
else if (PrimitiveSceneProxy->IsIndividuallySelected())
{
// Any component that is individually selected should have a stencil value of < 128 so that it can have a unique color. We offset the value by 2 because 0 means no selection and 1 is for bsp
StencilValue = ProxyToStencilIndex.Num() % 126 + 2;
ProxyToStencilIndex.Add(PrimitiveSceneProxy, StencilValue);
}
else
{
// If we are subduing actor color highlight then use the top level bits to indicate that to the shader.
StencilValue = bActorSelectionColorIsSubdued ? ActorNameToStencilIndex.Num() % 128 + 128 : ActorNameToStencilIndex.Num() % 126 + 2;
ActorNameToStencilIndex.Add(PrimitiveSceneProxy->GetOwnerName(), StencilValue);
}
return StencilValue;
}
FEditorSelectionMeshProcessor::FEditorSelectionMeshProcessor(const FScene* Scene, const FSceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext)
: FMeshPassProcessor(Scene, Scene->GetFeatureLevel(), InViewIfDynamicMeshCommand, InDrawListContext)
{
checkf(InViewIfDynamicMeshCommand, TEXT("Editor selection mesh process required dynamic mesh command mode."));
ActorNameToStencilIndex.Add(NAME_BSP, 1);
PassDrawRenderState.SetDepthStencilState(TStaticDepthStencilState<true, CF_DepthNearOrEqual, true, CF_Always, SO_Keep, SO_Keep, SO_Replace>::GetRHI());
PassDrawRenderState.SetBlendState(TStaticBlendStateWriteMask<CW_NONE, CW_NONE, CW_NONE, CW_NONE>::GetRHI());
}
FMeshPassProcessor* CreateEditorSelectionPassProcessor(const FScene* Scene, const FSceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext)
{
return new(FMemStack::Get()) FEditorSelectionMeshProcessor(Scene, InViewIfDynamicMeshCommand, InDrawListContext);
}
FRegisterPassProcessorCreateFunction RegisterEditorSelectionPass(&CreateEditorSelectionPassProcessor, EShadingPath::Deferred, EMeshPass::EditorSelection, EMeshPassFlags::MainView);
FRegisterPassProcessorCreateFunction RegisterMobileEditorSelectionPass(&CreateEditorSelectionPassProcessor, EShadingPath::Mobile, EMeshPass::EditorSelection, EMeshPassFlags::MainView);
void FEditorLevelInstanceMeshProcessor::AddMeshBatch(const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, int32 StaticMeshId)
{
if (MeshBatch.bUseForMaterial
&& PrimitiveSceneProxy
&& PrimitiveSceneProxy->IsEditingLevelInstanceChild())
{
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);
}
}
}
bool FEditorLevelInstanceMeshProcessor::TryAddMeshBatch(
const FMeshBatch& RESTRICT MeshBatch,
uint64 BatchElementMask,
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
int32 StaticMeshId,
const FMaterialRenderProxy* MaterialRenderProxy,
const FMaterial* Material)
{
// Determine the mesh's material and blend mode.
const FMeshDrawingPolicyOverrideSettings OverrideSettings = ComputeMeshOverrideSettings(MeshBatch);
const ERasterizerFillMode MeshFillMode = ComputeMeshFillMode(MeshBatch, *Material, OverrideSettings);
const ERasterizerCullMode MeshCullMode = CM_None;
if (Material->WritesEveryPixel() && !Material->IsTwoSided() && !Material->MaterialModifiesMeshPosition_RenderThread())
{
// Default material doesn't handle masked, and doesn't have the correct bIsTwoSided setting.
MaterialRenderProxy = UMaterial::GetDefaultMaterial(MD_Surface)->GetRenderProxy();
check(MaterialRenderProxy);
Material = MaterialRenderProxy->GetMaterialNoFallback(FeatureLevel);
}
check(Material && MaterialRenderProxy);
return Process(MeshBatch, BatchElementMask, StaticMeshId, PrimitiveSceneProxy, *MaterialRenderProxy, *Material, MeshFillMode, MeshCullMode);
}
bool FEditorLevelInstanceMeshProcessor::Process(
const FMeshBatch& MeshBatch,
uint64 BatchElementMask,
int32 StaticMeshId,
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
const FMaterialRenderProxy& RESTRICT MaterialRenderProxy,
const FMaterial& RESTRICT MaterialResource,
ERasterizerFillMode MeshFillMode,
ERasterizerCullMode MeshCullMode)
{
const FVertexFactory* VertexFactory = MeshBatch.VertexFactory;
TMeshProcessorShaders<
FHitProxyVS,
FHitProxyPS> HitProxyPassShaders;
if (!GetHitProxyPassShaders(
MaterialResource,
VertexFactory->GetType(),
FeatureLevel,
HitProxyPassShaders.VertexShader,
HitProxyPassShaders.PixelShader
))
{
return false;
}
const int32 StencilRef = GetStencilValue(ViewIfDynamicMeshCommand, PrimitiveSceneProxy);
PassDrawRenderState.SetStencilRef(StencilRef);
FHitProxyId DummyId;
FHitProxyShaderElementData ShaderElementData(DummyId);
ShaderElementData.InitializeMeshMaterialData(ViewIfDynamicMeshCommand, PrimitiveSceneProxy, MeshBatch, StaticMeshId, false);
const FMeshDrawCommandSortKey SortKey = CalculateMeshStaticSortKey(HitProxyPassShaders.VertexShader, HitProxyPassShaders.PixelShader);
BuildMeshDrawCommands(
MeshBatch,
BatchElementMask,
PrimitiveSceneProxy,
MaterialRenderProxy,
MaterialResource,
PassDrawRenderState,
HitProxyPassShaders,
MeshFillMode,
MeshCullMode,
SortKey,
EMeshPassFeatures::Default,
ShaderElementData);
return true;
}
int32 FEditorLevelInstanceMeshProcessor::GetStencilValue(const FSceneView* View, const FPrimitiveSceneProxy* PrimitiveSceneProxy)
{
// Set the stencil value to 1 for primitives which belong to an editing level instance, 0 otherwise
return PrimitiveSceneProxy->IsEditingLevelInstanceChild() ? 1 : 0;
}
FEditorLevelInstanceMeshProcessor::FEditorLevelInstanceMeshProcessor(const FScene* Scene, const FSceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext)
: FMeshPassProcessor(Scene, Scene->GetFeatureLevel(), InViewIfDynamicMeshCommand, InDrawListContext)
{
checkf(InViewIfDynamicMeshCommand, TEXT("Editor selection mesh process required dynamic mesh command mode."));
PassDrawRenderState.SetDepthStencilState(TStaticDepthStencilState<true, CF_DepthNearOrEqual, true, CF_Always, SO_Keep, SO_Keep, SO_Replace>::GetRHI());
PassDrawRenderState.SetBlendState(TStaticBlendStateWriteMask<CW_NONE, CW_NONE, CW_NONE, CW_NONE>::GetRHI());
}
FMeshPassProcessor* CreateEditorLevelInstancePassProcessor(const FScene* Scene, const FSceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext)
{
return new(FMemStack::Get()) FEditorLevelInstanceMeshProcessor(Scene, InViewIfDynamicMeshCommand, InDrawListContext);
}
FRegisterPassProcessorCreateFunction RegisterEditorLevelInstancePass(&CreateEditorLevelInstancePassProcessor, EShadingPath::Deferred, EMeshPass::EditorLevelInstance, EMeshPassFlags::MainView);
FRegisterPassProcessorCreateFunction RegisterMobileEditorLevelInstancePass(&CreateEditorLevelInstancePassProcessor, EShadingPath::Mobile, EMeshPass::EditorLevelInstance, EMeshPassFlags::MainView);
#endif