Be careful when setting sort values. Setting too many sort values on too many different decals prevents those decals from being sorted via state, which can harm performance.
Do you have any info on how this performance hit scales, how big it is, and in which scenarios…? What amount of Sort Order values are too many?
Let’s say that I need ~50 decals in the scene, each with unique Sort Order (sort order specified by the end-user) because otherwise they fight with each other. Most of them will not overlap with each other, but they can, when actors that they are attached to move. - hence the need for unique Sort Order values.
Is this aforementioned performance hit ‘real’ only when considering decals that overlap?
This seems to be the algorithm that sorts the decals in DecalRenderingShared.cpp
void SortDecalList(FTransientDecalRenderDataList& Decals)
{
// Sort by sort order to allow control over composited result
// Then sort decals by state to reduce render target switches
// Also sort by component since Sort() is not stable
struct FCompareFTransientDecalRenderData
{
FORCEINLINE bool operator()(const FTransientDecalRenderData& A, const FTransientDecalRenderData& B) const
{
if (B.Proxy.SortOrder != A.Proxy.SortOrder)
{
return A.Proxy.SortOrder < B.Proxy.SortOrder;
}
if (B.BlendDesc.bWriteNormal != A.BlendDesc.bWriteNormal)
{
// bWriteNormal here has priority because we want to render decals that output normals before those could read normals.
// Also this is the only flag that can trigger a change of EDecalRenderTargetMode inside a single EDecalRenderStage, and we batch according to this.
return B.BlendDesc.bWriteNormal < A.BlendDesc.bWriteNormal; // < so that those outputting normal are first.
}
if (B.BlendDesc.Packed != A.BlendDesc.Packed)
{
// Sorting by the FDecalBlendDesc contents will reduce blend state changes.
return (int32)B.BlendDesc.Packed < (int32)A.BlendDesc.Packed;
}
if (B.MaterialProxy != A.MaterialProxy)
{
// Batch decals with the same material together
return B.MaterialProxy < A.MaterialProxy;
}
return (PTRINT)B.Proxy.Component < (PTRINT)A.Proxy.Component;
}
};
// Sort decals by blend mode to reduce render target switches
Decals.Sort(FCompareFTransientDecalRenderData());
}
So the sorting isn’t super complex (just a comparison op implementation and sort) but the more indexes it has to sort the longer it takes.
I’m guessing this has to happen every time the decal actor’s render is marked as dirty or a decal actor is removed from the renderer.
It’s hard to see the context in which SortDecalList is triggered (is it cached etc). I’m guessing it’s somewhere in the rendering library on a lower level maybe packed in dll. (not visible in source)
Thanks! I wonder if the sorting function runs only for groups of overlapping decals or for all decals in a scene.
But it looks like the performance impact should not be really noticeable in most cases if the sorting is not that complex.