Files
UnrealEngineUWP/Engine/Source/Runtime/GeometryFramework/Private/Components/BaseDynamicMeshSceneProxy.cpp
Jeremy Moore ff967b1163 Remove PRIMITIVE_SCENE_DATA_FLAG_DRAWS_VELOCITY and only use PRIMITIVE_SCENE_DATA_FLAG_OUTPUT_VELOCITY.
Tidy up use of FPrimitiveSceneProxy velocity getters.
Now DrawsVelocity() is only for velocity relevance.
And HasDynamicTransform() is for determining if we need to store previous transform state.
VSM caching was using PRIMITIVE_SCENE_DATA_FLAG_DRAWS_VELOCITY so replaced that with PRIMITIVE_SCENE_DATA_FLAG_SHOULD_CACHE_SHADOW which should use the old behavior and can be tweaked in future using ShouldCacheShadow().

A common pattern for determining whether to output velocity now is to check if a previous transform exists, and to OR in AlwaysHasVelocity().
I found some proxy types that don't check for previous transform.
Also I found that the debug physics aggregate types *never* check for previous transform.
These are pre-existing potential bugs for fixing in another pass.

Also I found some proxies that don't currently fill out velocity relevance.
These are pre-existing potential bugs for fixing in another pass.

#preflight 62863f789016c6dd897f1cd2
#fyi andrew.lauritzen

[CL 20279797 by Jeremy Moore in ue5-main branch]
2022-05-19 10:08:15 -04:00

312 lines
13 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Components/BaseDynamicMeshSceneProxy.h"
#include "Materials/Material.h"
#include "RayTracingDefinitions.h"
#include "RayTracingInstance.h"
FBaseDynamicMeshSceneProxy::FBaseDynamicMeshSceneProxy(UBaseDynamicMeshComponent* Component)
: FPrimitiveSceneProxy(Component),
ParentBaseComponent(Component),
bEnableRaytracing(Component->GetEnableRaytracing()),
bEnableViewModeOverrides(Component->GetViewModeOverridesEnabled())
{
}
FBaseDynamicMeshSceneProxy::~FBaseDynamicMeshSceneProxy()
{
// we are assuming in code below that this is always called from the rendering thread
check(IsInRenderingThread());
// destroy all existing renderbuffers
for (FMeshRenderBufferSet* BufferSet : AllocatedBufferSets)
{
FMeshRenderBufferSet::DestroyRenderBufferSet(BufferSet);
}
}
FMeshRenderBufferSet* FBaseDynamicMeshSceneProxy::AllocateNewRenderBufferSet()
{
// should we hang onto these and destroy them in constructor? leaving to subclass seems risky?
FMeshRenderBufferSet* RenderBufferSet = new FMeshRenderBufferSet(GetScene().GetFeatureLevel());
RenderBufferSet->Material = UMaterial::GetDefaultMaterial(MD_Surface);
RenderBufferSet->bEnableRaytracing = this->bEnableRaytracing;
AllocatedSetsLock.Lock();
AllocatedBufferSets.Add(RenderBufferSet);
AllocatedSetsLock.Unlock();
return RenderBufferSet;
}
void FBaseDynamicMeshSceneProxy::ReleaseRenderBufferSet(FMeshRenderBufferSet* BufferSet)
{
FScopeLock Lock(&AllocatedSetsLock);
if (ensure(AllocatedBufferSets.Contains(BufferSet)))
{
AllocatedBufferSets.Remove(BufferSet);
Lock.Unlock();
FMeshRenderBufferSet::DestroyRenderBufferSet(BufferSet);
}
}
int32 FBaseDynamicMeshSceneProxy::GetNumMaterials() const
{
return ParentBaseComponent->GetNumMaterials();
}
UMaterialInterface* FBaseDynamicMeshSceneProxy::GetMaterial(int32 k) const
{
UMaterialInterface* Material = ParentBaseComponent->GetMaterial(k);
return (Material != nullptr) ? Material : UMaterial::GetDefaultMaterial(MD_Surface);
}
void FBaseDynamicMeshSceneProxy::UpdatedReferencedMaterials()
{
#if WITH_EDITOR
TArray<UMaterialInterface*> Materials;
ParentBaseComponent->GetUsedMaterials(Materials, true);
// Temporarily disable material verification while the enqueued render command is in flight.
// The original value for bVerifyUsedMaterials gets restored when the command is executed.
// If we do not do this, material verification might spuriously fail in cases where the render command for changing
// the verfifcation material is still in flight but the render thread is already trying to render the mesh.
const uint8 bRestoreVerifyUsedMaterials = bVerifyUsedMaterials;
bVerifyUsedMaterials = false;
ENQUEUE_RENDER_COMMAND(FMeshRenderBufferSetDestroy)(
[this, Materials, bRestoreVerifyUsedMaterials](FRHICommandListImmediate& RHICmdList)
{
this->SetUsedMaterialForVerification(Materials);
this->bVerifyUsedMaterials = bRestoreVerifyUsedMaterials;
});
#endif
}
void FBaseDynamicMeshSceneProxy::GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_BaseDynamicMeshSceneProxy_GetDynamicMeshElements);
const bool bWireframe = (AllowDebugViewmodes() && ViewFamily.EngineShowFlags.Wireframe)
|| ParentBaseComponent->GetEnableWireframeRenderPass();
// set up wireframe material. Probably bad to reference GEngine here...also this material is very bad?
FMaterialRenderProxy* WireframeMaterialProxy = nullptr;
if (bWireframe)
{
FColoredMaterialRenderProxy* WireframeMaterialInstance = new FColoredMaterialRenderProxy(
GEngine->WireframeMaterial ? GEngine->WireframeMaterial->GetRenderProxy() : nullptr,
FLinearColor(0, 0.5f, 1.f)
);
Collector.RegisterOneFrameMaterialProxy(WireframeMaterialInstance);
WireframeMaterialProxy = WireframeMaterialInstance;
}
ESceneDepthPriorityGroup DepthPriority = SDPG_World;
TArray<FMeshRenderBufferSet*> Buffers;
GetActiveRenderBufferSets(Buffers);
FMaterialRenderProxy* SecondaryMaterialProxy =
ParentBaseComponent->HasSecondaryRenderMaterial() ? ParentBaseComponent->GetSecondaryRenderMaterial()->GetRenderProxy() : nullptr;
bool bDrawSecondaryBuffers = ParentBaseComponent->GetSecondaryBuffersVisibility();
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
if (VisibilityMap & (1 << ViewIndex))
{
const FSceneView* View = Views[ViewIndex];
bool bHasPrecomputedVolumetricLightmap;
FMatrix PreviousLocalToWorld;
int32 SingleCaptureIndex;
bool bOutputVelocity;
GetScene().GetPrimitiveUniformShaderParameters_RenderThread(GetPrimitiveSceneInfo(), bHasPrecomputedVolumetricLightmap, PreviousLocalToWorld, SingleCaptureIndex, bOutputVelocity);
bOutputVelocity |= AlwaysHasVelocity();
// Draw the mesh.
for (FMeshRenderBufferSet* BufferSet : Buffers)
{
UMaterialInterface* UseMaterial = BufferSet->Material;
if (ParentBaseComponent->HasOverrideRenderMaterial(0))
{
UseMaterial = ParentBaseComponent->GetOverrideRenderMaterial(0);
}
FMaterialRenderProxy* MaterialProxy = UseMaterial->GetRenderProxy();
if (BufferSet->TriangleCount == 0)
{
continue;
}
// lock buffers so that they aren't modified while we are submitting them
FScopeLock BuffersLock(&BufferSet->BuffersLock);
// do we need separate one of these for each MeshRenderBufferSet?
FDynamicPrimitiveUniformBuffer& DynamicPrimitiveUniformBuffer = Collector.AllocateOneFrameResource<FDynamicPrimitiveUniformBuffer>();
DynamicPrimitiveUniformBuffer.Set(GetLocalToWorld(), PreviousLocalToWorld, GetBounds(), GetLocalBounds(), GetLocalBounds(), true, bHasPrecomputedVolumetricLightmap, bOutputVelocity, GetCustomPrimitiveData());
if (BufferSet->IndexBuffer.Indices.Num() > 0)
{
// Unlike most meshes, which just use the wireframe material in wireframe mode, we draw the wireframe on top of the normal material if needed,
// as this is easier to interpret. However, we do not do this in ortho viewports, where it frequently causes the our edit gizmo to be hidden
// beneath the material. So, only draw the base material if we are in perspective mode, or we're in ortho but not in wireframe.
if (View->IsPerspectiveProjection() || !(AllowDebugViewmodes() && ViewFamily.EngineShowFlags.Wireframe))
{
DrawBatch(Collector, *BufferSet, BufferSet->IndexBuffer, MaterialProxy, false, DepthPriority, ViewIndex, DynamicPrimitiveUniformBuffer);
}
if (bWireframe)
{
DrawBatch(Collector, *BufferSet, BufferSet->IndexBuffer, WireframeMaterialProxy, true, DepthPriority, ViewIndex, DynamicPrimitiveUniformBuffer);
}
}
// draw secondary buffer if we have it, falling back to base material if we don't have the Secondary material
FMaterialRenderProxy* UseSecondaryMaterialProxy = (SecondaryMaterialProxy != nullptr) ? SecondaryMaterialProxy : MaterialProxy;
if (bDrawSecondaryBuffers && BufferSet->SecondaryIndexBuffer.Indices.Num() > 0 && UseSecondaryMaterialProxy != nullptr)
{
DrawBatch(Collector, *BufferSet, BufferSet->SecondaryIndexBuffer, UseSecondaryMaterialProxy, false, DepthPriority, ViewIndex, DynamicPrimitiveUniformBuffer);
if (bWireframe)
{
DrawBatch(Collector, *BufferSet, BufferSet->SecondaryIndexBuffer, UseSecondaryMaterialProxy, true, DepthPriority, ViewIndex, DynamicPrimitiveUniformBuffer);
}
}
}
}
}
}
void FBaseDynamicMeshSceneProxy::DrawBatch(FMeshElementCollector& Collector, const FMeshRenderBufferSet& RenderBuffers, const FDynamicMeshIndexBuffer32& IndexBuffer, FMaterialRenderProxy* UseMaterial, bool bWireframe, ESceneDepthPriorityGroup DepthPriority, int ViewIndex, FDynamicPrimitiveUniformBuffer& DynamicPrimitiveUniformBuffer) const
{
FMeshBatch& Mesh = Collector.AllocateMesh();
FMeshBatchElement& BatchElement = Mesh.Elements[0];
BatchElement.IndexBuffer = &IndexBuffer;
Mesh.bWireframe = bWireframe;
Mesh.VertexFactory = &RenderBuffers.VertexFactory;
Mesh.MaterialRenderProxy = UseMaterial;
BatchElement.PrimitiveUniformBufferResource = &DynamicPrimitiveUniformBuffer.UniformBuffer;
BatchElement.FirstIndex = 0;
BatchElement.NumPrimitives = IndexBuffer.Indices.Num() / 3;
BatchElement.MinVertexIndex = 0;
BatchElement.MaxVertexIndex = RenderBuffers.PositionVertexBuffer.GetNumVertices() - 1;
Mesh.ReverseCulling = IsLocalToWorldDeterminantNegative();
Mesh.Type = PT_TriangleList;
Mesh.DepthPriorityGroup = DepthPriority;
Mesh.bCanApplyViewModeOverrides = this->bEnableViewModeOverrides;
Collector.AddMesh(ViewIndex, Mesh);
}
#if RHI_RAYTRACING
bool FBaseDynamicMeshSceneProxy::IsRayTracingRelevant() const
{
return true;
}
void FBaseDynamicMeshSceneProxy::GetDynamicRayTracingInstances(FRayTracingMaterialGatheringContext& Context, TArray<FRayTracingInstance>& OutRayTracingInstances)
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_BaseDynamicMeshSceneProxy_GetDynamicRayTracingInstances);
ESceneDepthPriorityGroup DepthPriority = SDPG_World;
TArray<FMeshRenderBufferSet*> Buffers;
GetActiveRenderBufferSets(Buffers);
FMaterialRenderProxy* SecondaryMaterialProxy =
ParentBaseComponent->HasSecondaryRenderMaterial() ? ParentBaseComponent->GetSecondaryRenderMaterial()->GetRenderProxy() : nullptr;
bool bDrawSecondaryBuffers = ParentBaseComponent->GetSecondaryBuffersVisibility();
bool bHasPrecomputedVolumetricLightmap;
FMatrix PreviousLocalToWorld;
int32 SingleCaptureIndex;
bool bOutputVelocity;
GetScene().GetPrimitiveUniformShaderParameters_RenderThread(GetPrimitiveSceneInfo(), bHasPrecomputedVolumetricLightmap, PreviousLocalToWorld, SingleCaptureIndex, bOutputVelocity);
bOutputVelocity |= AlwaysHasVelocity();
// is it safe to share this between primary and secondary raytracing batches?
FDynamicPrimitiveUniformBuffer& DynamicPrimitiveUniformBuffer = Context.RayTracingMeshResourceCollector.AllocateOneFrameResource<FDynamicPrimitiveUniformBuffer>();
DynamicPrimitiveUniformBuffer.Set(GetLocalToWorld(), PreviousLocalToWorld, GetBounds(), GetLocalBounds(), true, bHasPrecomputedVolumetricLightmap, bOutputVelocity);
// Draw the active buffer sets
for (FMeshRenderBufferSet* BufferSet : Buffers)
{
UMaterialInterface* UseMaterial = BufferSet->Material;
if (ParentBaseComponent->HasOverrideRenderMaterial(0))
{
UseMaterial = ParentBaseComponent->GetOverrideRenderMaterial(0);
}
FMaterialRenderProxy* MaterialProxy = UseMaterial->GetRenderProxy();
if (BufferSet->TriangleCount == 0)
{
continue;
}
if (BufferSet->bIsRayTracingDataValid == false)
{
continue;
}
// Lock buffers so that they aren't modified while we are submitting them.
FScopeLock BuffersLock(&BufferSet->BuffersLock);
// draw primary index buffer
if (BufferSet->IndexBuffer.Indices.Num() > 0
&& BufferSet->PrimaryRayTracingGeometry.RayTracingGeometryRHI.IsValid())
{
ensure(BufferSet->PrimaryRayTracingGeometry.Initializer.IndexBuffer.IsValid());
DrawRayTracingBatch(Context, *BufferSet, BufferSet->IndexBuffer, BufferSet->PrimaryRayTracingGeometry, MaterialProxy, DepthPriority, DynamicPrimitiveUniformBuffer, OutRayTracingInstances);
}
// draw secondary index buffer if we have it, falling back to base material if we don't have the Secondary material
FMaterialRenderProxy* UseSecondaryMaterialProxy = (SecondaryMaterialProxy != nullptr) ? SecondaryMaterialProxy : MaterialProxy;
if (bDrawSecondaryBuffers
&& BufferSet->SecondaryIndexBuffer.Indices.Num() > 0
&& UseSecondaryMaterialProxy != nullptr
&& BufferSet->SecondaryRayTracingGeometry.RayTracingGeometryRHI.IsValid())
{
ensure(BufferSet->SecondaryRayTracingGeometry.Initializer.IndexBuffer.IsValid());
DrawRayTracingBatch(Context, *BufferSet, BufferSet->SecondaryIndexBuffer, BufferSet->SecondaryRayTracingGeometry, UseSecondaryMaterialProxy, DepthPriority, DynamicPrimitiveUniformBuffer, OutRayTracingInstances);
}
}
}
void FBaseDynamicMeshSceneProxy::DrawRayTracingBatch(FRayTracingMaterialGatheringContext& Context, const FMeshRenderBufferSet& RenderBuffers, const FDynamicMeshIndexBuffer32& IndexBuffer, FRayTracingGeometry& RayTracingGeometry, FMaterialRenderProxy* UseMaterialProxy, ESceneDepthPriorityGroup DepthPriority, FDynamicPrimitiveUniformBuffer& DynamicPrimitiveUniformBuffer, TArray<FRayTracingInstance>& OutRayTracingInstances) const
{
ensure(RayTracingGeometry.Initializer.IndexBuffer.IsValid());
FRayTracingInstance RayTracingInstance;
RayTracingInstance.Geometry = &RayTracingGeometry;
RayTracingInstance.InstanceTransforms.Add(GetLocalToWorld());
uint32 SectionIdx = 0;
FMeshBatch MeshBatch;
MeshBatch.VertexFactory = &RenderBuffers.VertexFactory;
MeshBatch.SegmentIndex = 0;
MeshBatch.MaterialRenderProxy = UseMaterialProxy;
MeshBatch.ReverseCulling = IsLocalToWorldDeterminantNegative();
MeshBatch.Type = PT_TriangleList;
MeshBatch.DepthPriorityGroup = DepthPriority;
MeshBatch.bCanApplyViewModeOverrides = this->bEnableViewModeOverrides;
MeshBatch.CastRayTracedShadow = IsShadowCast(Context.ReferenceView);
FMeshBatchElement& BatchElement = MeshBatch.Elements[0];
BatchElement.IndexBuffer = &IndexBuffer;
BatchElement.PrimitiveUniformBufferResource = &DynamicPrimitiveUniformBuffer.UniformBuffer;
BatchElement.FirstIndex = 0;
BatchElement.NumPrimitives = IndexBuffer.Indices.Num() / 3;
BatchElement.MinVertexIndex = 0;
BatchElement.MaxVertexIndex = RenderBuffers.PositionVertexBuffer.GetNumVertices() - 1;
RayTracingInstance.Materials.Add(MeshBatch);
RayTracingInstance.BuildInstanceMaskAndFlags(GetScene().GetFeatureLevel());
OutRayTracingInstances.Add(RayTracingInstance);
}
#endif // RHI_RAYTRACING