Files
UnrealEngineUWP/Engine/Source/Runtime/Renderer/Private/SceneHitProxyRendering.cpp
jason hoerner 6144ebf450 Scene renderer no longer contains multiple view families. Instead, multiple view families are handled by creating multiple scene renderers, which are added to a linked list with the first scene renderer as the list head. This more or less reverses CL 19813996, while preserving the optimizations gained from having knowledge of multiple scene renderers. This includes deferring cross GPU resource transfer waits to the last scene render, running ray tracing scene update once, and only flushing resources once. Future optimizations and bug fixes may take advantage of additional information from other scene renderers.
The refactor to get rid of the FSceneTextures and FSceneTexturesConfig global singletons has also been preserved.  The FViewFamilyInfo is used to hold those structures, so client facing API functions that lack a scene renderer reference can still pull the FSceneTextures[Config] from the view family pointer.  All other scene renderer state has been moved from FViewFamilyInfo back into FSceneRenderer.

#jira none
#rnx
#rb ola.olsson krzysztof.narkowicz zach.bethel
#preflight 629f770e233ae0a8f8fb7f2e

[CL 20540730 by jason hoerner in ue5-main branch]
2022-06-07 13:19:54 -04:00

1201 lines
48 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"
static int32 GNaniteProgrammableRasterHitProxy = 1;
static FAutoConsoleVariableRef CNaniteProgrammableRasterHitProxy(
TEXT("r.Nanite.ProgrammableRaster.HitProxy"),
GNaniteProgrammableRasterHitProxy,
TEXT("A toggle that allows Nanite programmable raster in hit proxy passes.\n")
TEXT(" 0: Programmable raster is disabled\n")
TEXT(" 1: Programmable raster is enabled (default)"),
ECVF_RenderThreadSafe);
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, FSceneRenderer* SceneRenderer, 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 FSceneTexturesConfig& SceneTexturesConfig = ViewFamily.SceneTexturesConfig;
FMinimalSceneTextures::InitializeViewFamily(GraphBuilder, SceneRenderer->ViewFamily);
const FMinimalSceneTextures& SceneTextures = ViewFamily.GetSceneTextures();
// 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->GetActiveSceneTextures(), 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::InitializeViewFamily(ViewFamily);
FSceneTexturesConfig& SceneTexturesConfig = GetActiveSceneTexturesConfig();
FSceneTexturesConfig::Set(SceneTexturesConfig);
FRDGTextureRef HitProxyTexture = nullptr;
FRDGTextureRef HitProxyDepthTexture = nullptr;
InitHitProxyRender(GraphBuilder, this, 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)
{
const bool bNaniteEnabled = UseNanite(ShaderPlatform);
Scene->UpdateAllPrimitiveSceneInfos(GraphBuilder);
GPU_MESSAGE_SCOPE(GraphBuilder);
FGPUSceneScopeBeginEndHelper GPUSceneScopeBeginEndHelper(Scene->GPUScene, GPUSceneDynamicContext, Scene);
PrepareViewRectsForRendering(GraphBuilder.RHICmdList);
#if WITH_EDITOR
FSceneTexturesConfig::InitializeViewFamily(ViewFamily);
FSceneTexturesConfig& SceneTexturesConfig = GetActiveSceneTexturesConfig();
FSceneTexturesConfig::Set(SceneTexturesConfig);
FRDGTextureRef HitProxyTexture = nullptr;
FRDGTextureRef HitProxyDepthTexture = nullptr;
InitHitProxyRender(GraphBuilder, this, HitProxyTexture, HitProxyDepthTexture);
const FIntPoint HitProxyTextureSize = HitProxyDepthTexture->Desc.Extent;
FInstanceCullingManager& InstanceCullingManager = *GraphBuilder.AllocObject<FInstanceCullingManager>(Scene->GPUScene.IsEnabled(), GraphBuilder);
// Find the visible primitives.
{
FLumenSceneFrameTemporaries LumenFrameTemporaries;
FILCUpdatePrimTaskData ILCTaskData;
InitViews(GraphBuilder, SceneTexturesConfig, FExclusiveDepthStencil::DepthWrite_StencilWrite, ILCTaskData, InstanceCullingManager);
InitViewsAfterPrepass(GraphBuilder, LumenFrameTemporaries, 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]);
}
}
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
ShaderPrint::BeginView(GraphBuilder, Views[ViewIndex]);
}
{
FRDGExternalAccessQueue ExternalAccessQueue;
Scene->GPUScene.Update(GraphBuilder, *Scene, ExternalAccessQueue);
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
Scene->GPUScene.UploadDynamicPrimitiveShaderDataForView(GraphBuilder, Scene, Views[ViewIndex], ExternalAccessQueue);
}
ExternalAccessQueue.Submit(GraphBuilder);
}
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;
CullingConfig.bProgrammableRaster = GNaniteProgrammableRasterHitProxy != 0;
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