From f7470af42dad4eb141692c8237567803bc6bc89d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 26 Mar 2024 18:04:08 +0100 Subject: [PATCH] Optimize depth pass rendering to batch simple materials together --- Source/Engine/Graphics/Materials/IMaterial.h | 2 +- Source/Engine/Renderer/RenderList.cpp | 47 ++++++++++++-------- Source/Engine/Renderer/RenderList.h | 10 +++-- Source/Engine/Renderer/Renderer.cpp | 6 +-- 4 files changed, 39 insertions(+), 26 deletions(-) diff --git a/Source/Engine/Graphics/Materials/IMaterial.h b/Source/Engine/Graphics/Materials/IMaterial.h index 83b6abf52..5235dd59e 100644 --- a/Source/Engine/Graphics/Materials/IMaterial.h +++ b/Source/Engine/Graphics/Materials/IMaterial.h @@ -119,7 +119,7 @@ public: struct InstancingHandler { 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); }; diff --git a/Source/Engine/Renderer/RenderList.cpp b/Source/Engine/Renderer/RenderList.cpp index 29b9bd622..efe3edd2a 100644 --- a/Source/Engine/Renderer/RenderList.cpp +++ b/Source/Engine/Renderer/RenderList.cpp @@ -551,26 +551,21 @@ void RenderList::AddDrawCall(const RenderContextBatch& renderContextBatch, DrawP namespace { - /// - /// Checks if this draw call be batched together with the other one. - /// - /// The first draw call. - /// The second draw call. - /// True if can merge them, otherwise false. - FORCE_INLINE bool CanBatchWith(const DrawCall& a, const DrawCall& b) + FORCE_INLINE bool CanBatchWith(const DrawCall& a, const DrawCall& b, DrawPass pass) { - IMaterial::InstancingHandler handler; - return a.Material == b.Material && - a.Material->CanUseInstancing(handler) && + IMaterial::InstancingHandler handlerA, handlerB; + return a.Material->CanUseInstancing(handlerA) && + b.Material->CanUseInstancing(handlerB) && Platform::MemoryCompare(&a.Geometry, &b.Geometry, sizeof(a.Geometry)) == 0 && a.InstanceCount != 0 && b.InstanceCount != 0 && - handler.CanBatch(a, b) && + handlerA.CanBatch == handlerB.CanBatch && + handlerA.CanBatch(a, b, pass) && a.WorldDeterminantSign * b.WorldDeterminantSign > 0; } } -void RenderList::SortDrawCalls(const RenderContext& renderContext, bool reverseDistance, DrawCallsList& list, const RenderListBuffer& drawCalls) +void RenderList::SortDrawCalls(const RenderContext& renderContext, bool reverseDistance, DrawCallsList& list, const RenderListBuffer& drawCalls, DrawPass pass) { PROFILE_CPU(); 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++) { const DrawCall& other = drawCallsData[listData[j]]; - if (!CanBatchWith(drawCall, other)) + if (!CanBatchWith(drawCall, other, pass)) break; batchSize++; instanceCount += other.InstanceCount; @@ -917,13 +912,29 @@ void SurfaceDrawCallHandler::GetHash(const DrawCall& drawCall, uint32& batchKey) 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) - return a.Surface.Lightmap == nullptr && b.Surface.Lightmap == nullptr && - //return a.Surface.Lightmap == b.Surface.Lightmap && - a.Surface.Skinning == nullptr && - b.Surface.Skinning == nullptr; + if (a.Surface.Lightmap == nullptr && b.Surface.Lightmap == nullptr && + //return a.Surface.Lightmap == b.Surface.Lightmap && + a.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) diff --git a/Source/Engine/Renderer/RenderList.h b/Source/Engine/Renderer/RenderList.h index 5f2aecbd9..15b5a9842 100644 --- a/Source/Engine/Renderer/RenderList.h +++ b/Source/Engine/Renderer/RenderList.h @@ -480,9 +480,10 @@ public: /// The rendering context. /// If set to true reverse draw call distance to the view. Results in back to front sorting. /// The collected draw calls list type. - API_FUNCTION() FORCE_INLINE void SortDrawCalls(API_PARAM(Ref) const RenderContext& renderContext, bool reverseDistance, DrawCallsListType listType) + /// The draw pass (optional). + 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); } /// @@ -492,7 +493,8 @@ public: /// If set to true reverse draw call distance to the view. Results in back to front sorting. /// The collected draw calls indices list. /// The collected draw calls list. - void SortDrawCalls(const RenderContext& renderContext, bool reverseDistance, DrawCallsList& list, const RenderListBuffer& drawCalls); + /// The draw pass (optional). + void SortDrawCalls(const RenderContext& renderContext, bool reverseDistance, DrawCallsList& list, const RenderListBuffer& drawCalls, DrawPass pass = DrawPass::All); /// /// Executes the collected draw calls. @@ -543,6 +545,6 @@ PACK_STRUCT(struct FLAXENGINE_API InstanceData struct SurfaceDrawCallHandler { 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); }; diff --git a/Source/Engine/Renderer/Renderer.cpp b/Source/Engine/Renderer/Renderer.cpp index 0ef39bb30..7500b2f1e 100644 --- a/Source/Engine/Renderer/Renderer.cpp +++ b/Source/Engine/Renderer/Renderer.cpp @@ -244,7 +244,7 @@ void Renderer::DrawSceneDepth(GPUContext* context, SceneRenderTask* task, GPUTex DrawActors(renderContext, customActors); // Sort draw calls - renderContext.List->SortDrawCalls(renderContext, false, DrawCallsListType::Depth); + renderContext.List->SortDrawCalls(renderContext, false, DrawCallsListType::Depth, DrawPass::Depth); // Execute draw calls 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++) { auto& shadowContext = renderContextBatch.Contexts[i]; - shadowContext.List->SortDrawCalls(shadowContext, false, DrawCallsListType::Depth); - shadowContext.List->SortDrawCalls(shadowContext, false, shadowContext.List->ShadowDepthDrawCallsList, renderContext.List->DrawCalls); + shadowContext.List->SortDrawCalls(shadowContext, false, DrawCallsListType::Depth, DrawPass::Depth); + shadowContext.List->SortDrawCalls(shadowContext, false, shadowContext.List->ShadowDepthDrawCallsList, renderContext.List->DrawCalls, DrawPass::Depth); } }