Optimize depth pass rendering to batch simple materials together

This commit is contained in:
Wojtek Figat
2024-03-26 18:04:08 +01:00
parent 5c356ec22a
commit f7470af42d
4 changed files with 39 additions and 26 deletions

View File

@@ -119,7 +119,7 @@ public:
struct InstancingHandler struct InstancingHandler
{ {
void (*GetHash)(const DrawCall& drawCall, uint32& batchKey); void (*GetHash)(const DrawCall& drawCall, uint32& batchKey);
bool (*CanBatch)(const DrawCall& a, const DrawCall& b); bool (*CanBatch)(const DrawCall& a, const DrawCall& b, DrawPass pass);
void (*WriteDrawCall)(struct InstanceData* instanceData, const DrawCall& drawCall); void (*WriteDrawCall)(struct InstanceData* instanceData, const DrawCall& drawCall);
}; };

View File

@@ -551,26 +551,21 @@ void RenderList::AddDrawCall(const RenderContextBatch& renderContextBatch, DrawP
namespace namespace
{ {
/// <summary> FORCE_INLINE bool CanBatchWith(const DrawCall& a, const DrawCall& b, DrawPass pass)
/// Checks if this draw call be batched together with the other one.
/// </summary>
/// <param name="a">The first draw call.</param>
/// <param name="b">The second draw call.</param>
/// <returns>True if can merge them, otherwise false.</returns>
FORCE_INLINE bool CanBatchWith(const DrawCall& a, const DrawCall& b)
{ {
IMaterial::InstancingHandler handler; IMaterial::InstancingHandler handlerA, handlerB;
return a.Material == b.Material && return a.Material->CanUseInstancing(handlerA) &&
a.Material->CanUseInstancing(handler) && b.Material->CanUseInstancing(handlerB) &&
Platform::MemoryCompare(&a.Geometry, &b.Geometry, sizeof(a.Geometry)) == 0 && Platform::MemoryCompare(&a.Geometry, &b.Geometry, sizeof(a.Geometry)) == 0 &&
a.InstanceCount != 0 && a.InstanceCount != 0 &&
b.InstanceCount != 0 && b.InstanceCount != 0 &&
handler.CanBatch(a, b) && handlerA.CanBatch == handlerB.CanBatch &&
handlerA.CanBatch(a, b, pass) &&
a.WorldDeterminantSign * b.WorldDeterminantSign > 0; a.WorldDeterminantSign * b.WorldDeterminantSign > 0;
} }
} }
void RenderList::SortDrawCalls(const RenderContext& renderContext, bool reverseDistance, DrawCallsList& list, const RenderListBuffer<DrawCall>& drawCalls) void RenderList::SortDrawCalls(const RenderContext& renderContext, bool reverseDistance, DrawCallsList& list, const RenderListBuffer<DrawCall>& drawCalls, DrawPass pass)
{ {
PROFILE_CPU(); PROFILE_CPU();
const auto* drawCallsData = drawCalls.Get(); const auto* drawCallsData = drawCalls.Get();
@@ -625,7 +620,7 @@ void RenderList::SortDrawCalls(const RenderContext& renderContext, bool reverseD
for (int32 j = i + 1; j < listSize; j++) for (int32 j = i + 1; j < listSize; j++)
{ {
const DrawCall& other = drawCallsData[listData[j]]; const DrawCall& other = drawCallsData[listData[j]];
if (!CanBatchWith(drawCall, other)) if (!CanBatchWith(drawCall, other, pass))
break; break;
batchSize++; batchSize++;
instanceCount += other.InstanceCount; instanceCount += other.InstanceCount;
@@ -917,13 +912,29 @@ void SurfaceDrawCallHandler::GetHash(const DrawCall& drawCall, uint32& batchKey)
batchKey = (batchKey * 397) ^ ::GetHash(drawCall.Surface.Lightmap); batchKey = (batchKey * 397) ^ ::GetHash(drawCall.Surface.Lightmap);
} }
bool SurfaceDrawCallHandler::CanBatch(const DrawCall& a, const DrawCall& b) bool SurfaceDrawCallHandler::CanBatch(const DrawCall& a, const DrawCall& b, DrawPass pass)
{ {
// TODO: find reason why batching static meshes with lightmap causes problems with sampling in shader (flickering when meshes in batch order gets changes due to async draw calls collection) // TODO: find reason why batching static meshes with lightmap causes problems with sampling in shader (flickering when meshes in batch order gets changes due to async draw calls collection)
return a.Surface.Lightmap == nullptr && b.Surface.Lightmap == nullptr && if (a.Surface.Lightmap == nullptr && b.Surface.Lightmap == nullptr &&
//return a.Surface.Lightmap == b.Surface.Lightmap && //return a.Surface.Lightmap == b.Surface.Lightmap &&
a.Surface.Skinning == nullptr && a.Surface.Skinning == nullptr &&
b.Surface.Skinning == nullptr; b.Surface.Skinning == nullptr)
{
if (a.Material != b.Material)
{
// Batch simple materials during depth-only drawing (when using default vertex shader and no pixel shader)
if (pass == DrawPass::Depth)
{
constexpr MaterialUsageFlags complexUsageFlags = MaterialUsageFlags::UseMask | MaterialUsageFlags::UsePositionOffset | MaterialUsageFlags::UseDisplacement;
const bool aIsSimple = EnumHasNoneFlags(a.Material->GetInfo().UsageFlags, complexUsageFlags);
const bool bIsSimple = EnumHasNoneFlags(b.Material->GetInfo().UsageFlags, complexUsageFlags);
return aIsSimple && bIsSimple;
}
return false;
}
return true;
}
return false;
} }
void SurfaceDrawCallHandler::WriteDrawCall(InstanceData* instanceData, const DrawCall& drawCall) void SurfaceDrawCallHandler::WriteDrawCall(InstanceData* instanceData, const DrawCall& drawCall)

View File

@@ -480,9 +480,10 @@ public:
/// <param name="renderContext">The rendering context.</param> /// <param name="renderContext">The rendering context.</param>
/// <param name="reverseDistance">If set to <c>true</c> reverse draw call distance to the view. Results in back to front sorting.</param> /// <param name="reverseDistance">If set to <c>true</c> reverse draw call distance to the view. Results in back to front sorting.</param>
/// <param name="listType">The collected draw calls list type.</param> /// <param name="listType">The collected draw calls list type.</param>
API_FUNCTION() FORCE_INLINE void SortDrawCalls(API_PARAM(Ref) const RenderContext& renderContext, bool reverseDistance, DrawCallsListType listType) /// <param name="pass">The draw pass (optional).</param>
API_FUNCTION() FORCE_INLINE void SortDrawCalls(API_PARAM(Ref) const RenderContext& renderContext, bool reverseDistance, DrawCallsListType listType, DrawPass pass = DrawPass::All)
{ {
SortDrawCalls(renderContext, reverseDistance, DrawCallsLists[(int32)listType], DrawCalls); SortDrawCalls(renderContext, reverseDistance, DrawCallsLists[(int32)listType], DrawCalls, pass);
} }
/// <summary> /// <summary>
@@ -492,7 +493,8 @@ public:
/// <param name="reverseDistance">If set to <c>true</c> reverse draw call distance to the view. Results in back to front sorting.</param> /// <param name="reverseDistance">If set to <c>true</c> reverse draw call distance to the view. Results in back to front sorting.</param>
/// <param name="list">The collected draw calls indices list.</param> /// <param name="list">The collected draw calls indices list.</param>
/// <param name="drawCalls">The collected draw calls list.</param> /// <param name="drawCalls">The collected draw calls list.</param>
void SortDrawCalls(const RenderContext& renderContext, bool reverseDistance, DrawCallsList& list, const RenderListBuffer<DrawCall>& drawCalls); /// <param name="pass">The draw pass (optional).</param>
void SortDrawCalls(const RenderContext& renderContext, bool reverseDistance, DrawCallsList& list, const RenderListBuffer<DrawCall>& drawCalls, DrawPass pass = DrawPass::All);
/// <summary> /// <summary>
/// Executes the collected draw calls. /// Executes the collected draw calls.
@@ -543,6 +545,6 @@ PACK_STRUCT(struct FLAXENGINE_API InstanceData
struct SurfaceDrawCallHandler struct SurfaceDrawCallHandler
{ {
static void GetHash(const DrawCall& drawCall, uint32& batchKey); static void GetHash(const DrawCall& drawCall, uint32& batchKey);
static bool CanBatch(const DrawCall& a, const DrawCall& b); static bool CanBatch(const DrawCall& a, const DrawCall& b, DrawPass pass);
static void WriteDrawCall(InstanceData* instanceData, const DrawCall& drawCall); static void WriteDrawCall(InstanceData* instanceData, const DrawCall& drawCall);
}; };

View File

@@ -244,7 +244,7 @@ void Renderer::DrawSceneDepth(GPUContext* context, SceneRenderTask* task, GPUTex
DrawActors(renderContext, customActors); DrawActors(renderContext, customActors);
// Sort draw calls // Sort draw calls
renderContext.List->SortDrawCalls(renderContext, false, DrawCallsListType::Depth); renderContext.List->SortDrawCalls(renderContext, false, DrawCallsListType::Depth, DrawPass::Depth);
// Execute draw calls // Execute draw calls
const float width = (float)output->Width(); const float width = (float)output->Width();
@@ -405,8 +405,8 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderCont
for (int32 i = 1; i < renderContextBatch.Contexts.Count(); i++) for (int32 i = 1; i < renderContextBatch.Contexts.Count(); i++)
{ {
auto& shadowContext = renderContextBatch.Contexts[i]; auto& shadowContext = renderContextBatch.Contexts[i];
shadowContext.List->SortDrawCalls(shadowContext, false, DrawCallsListType::Depth); shadowContext.List->SortDrawCalls(shadowContext, false, DrawCallsListType::Depth, DrawPass::Depth);
shadowContext.List->SortDrawCalls(shadowContext, false, shadowContext.List->ShadowDepthDrawCallsList, renderContext.List->DrawCalls); shadowContext.List->SortDrawCalls(shadowContext, false, shadowContext.List->ShadowDepthDrawCallsList, renderContext.List->DrawCalls, DrawPass::Depth);
} }
} }