From f4905cfcccfd210818d4be8e7c721a8084bfc6a1 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 28 Apr 2026 00:24:05 +0200 Subject: [PATCH] Optimize foliage rendering with less cache misses --- Source/Engine/Foliage/Foliage.cpp | 36 +++++++++++++++---------------- Source/Engine/Foliage/Foliage.h | 2 +- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Source/Engine/Foliage/Foliage.cpp b/Source/Engine/Foliage/Foliage.cpp index 221f456bb..dd6c442b1 100644 --- a/Source/Engine/Foliage/Foliage.cpp +++ b/Source/Engine/Foliage/Foliage.cpp @@ -130,26 +130,25 @@ void Foliage::AddToCluster(ChunkedArrayLODs.Get()[lod].Meshes; - for (int32 meshIndex = 0; meshIndex < meshes.Count(); meshIndex++) + const auto& drawCalls = drawCallsLists[lod]; + const auto* drawCallsPtr = drawCalls.Get(); + for (int32 i = 0; i < drawCalls.Count(); i++) { - auto& drawCall = drawCallsLists[lod][meshIndex]; - if (!drawCall.Material) - continue; + auto& drawCall = drawCallsPtr[i]; DrawKey key; key.Mat = drawCall.Material; - key.Geo = &meshes.Get()[meshIndex]; + key.Geo = (Mesh*)(void*)drawCall.Geometry.IndexBuffer; // Hack to pass this over Foliage::DrawType key.Lightmap = instance.LightmapTextureIndex; auto* e = result.TryGet(key); if (!e) { e = &result.Add(key, BatchedDrawCall(context.RenderContext.List))->Value; e->DrawCall.Material = key.Mat; - e->DrawCall.Surface.Lightmap = EnumHasAnyFlags(_staticFlags, StaticFlags::Lightmap) && _scene ? _scene->LightmapsData.GetReadyLightmap(key.Lightmap) : nullptr; - e->DrawCall.Surface.GeometrySize = key.Geo->GetBox().GetSize(); + e->DrawCall.Surface.Lightmap = key.Lightmap != -1 && EnumHasAnyFlags(_staticFlags, StaticFlags::Lightmap) && _scene ? _scene->LightmapsData.GetReadyLightmap(key.Lightmap) : nullptr; + e->DrawCall.Surface.GeometrySize = drawCall.Surface.GeometrySize; } // Add instance to the draw batch @@ -231,14 +230,14 @@ void Foliage::DrawCluster(DrawContext& context, FoliageCluster* cluster, DrawCal { const auto prevLOD = model->ClampLODIndex(instance.DrawStatePrevLOD); const float normalizedProgress = static_cast(instance.DrawStateLODTransition) * (1.0f / 255.0f); - DrawInstance(context, instance, model, prevLOD, normalizedProgress, drawCallsLists, result); + DrawInstance(context, instance, prevLOD, normalizedProgress, drawCallsLists, result); } } else if (instance.DrawStateLODTransition < 255) { const auto prevLOD = model->ClampLODIndex(instance.DrawStatePrevLOD); const float normalizedProgress = static_cast(instance.DrawStateLODTransition) * (1.0f / 255.0f); - DrawInstance(context, instance, model, prevLOD, normalizedProgress, drawCallsLists, result); + DrawInstance(context, instance, prevLOD, normalizedProgress, drawCallsLists, result); } } instance.DrawStatePrevFrame = frame; @@ -278,19 +277,19 @@ void Foliage::DrawCluster(DrawContext& context, FoliageCluster* cluster, DrawCal // Draw if (instance.DrawStatePrevLOD == lodIndex) { - DrawInstance(context, instance, model, lodIndex, 0.0f, drawCallsLists, result); + DrawInstance(context, instance, lodIndex, 0.0f, drawCallsLists, result); } else if (instance.DrawStatePrevLOD == -1) { const float normalizedProgress = static_cast(instance.DrawStateLODTransition) * (1.0f / 255.0f); - DrawInstance(context, instance, model, lodIndex, 1.0f - normalizedProgress, drawCallsLists, result); + DrawInstance(context, instance, lodIndex, 1.0f - normalizedProgress, drawCallsLists, result); } else { const auto prevLOD = model->ClampLODIndex(instance.DrawStatePrevLOD); const float normalizedProgress = static_cast(instance.DrawStateLODTransition) * (1.0f / 255.0f); - DrawInstance(context, instance, model, prevLOD, normalizedProgress, drawCallsLists, result); - DrawInstance(context, instance, model, lodIndex, normalizedProgress - 1.0f, drawCallsLists, result); + DrawInstance(context, instance, prevLOD, normalizedProgress, drawCallsLists, result); + DrawInstance(context, instance, lodIndex, normalizedProgress - 1.0f, drawCallsLists, result); } //DebugDraw::DrawSphere(instance.Bounds, Color::YellowGreen); @@ -493,12 +492,10 @@ void Foliage::DrawType(RenderContext& renderContext, const FoliageType& type, Me auto& modelLod = type.Model->LODs[lod]; DrawCallsList& drawCallsList = drawCallsLists[lod]; const auto& meshes = modelLod.Meshes; - drawCallsList.Resize(meshes.Count()); + drawCallsList.EnsureCapacity(meshes.Count()); for (int32 meshIndex = 0; meshIndex < meshes.Count(); meshIndex++) { const auto& mesh = meshes.Get()[meshIndex]; - auto& drawCall = drawCallsList.Get()[meshIndex]; - drawCall.Material = nullptr; // DrawInstance skips draw calls from meshes with unset material // Check entry visibility const auto& entry = type.Entries[mesh.GetMaterialSlotIndex()]; @@ -523,7 +520,10 @@ void Foliage::DrawType(RenderContext& renderContext, const FoliageType& type, Me if (drawModes == DrawPass::None) continue; + // Add mesh to drawing + auto& drawCall = drawCallsList.AddOne(); drawCall.Material = material; + drawCall.Geometry.IndexBuffer = (GPUBuffer*)(void*)&mesh; // Wrap pointer to mesh for DrawKey::Geo drawCall.Surface.GeometrySize = mesh.GetBox().GetSize(); } } diff --git a/Source/Engine/Foliage/Foliage.h b/Source/Engine/Foliage/Foliage.h index 1eb775913..77e7b4ae1 100644 --- a/Source/Engine/Foliage/Foliage.h +++ b/Source/Engine/Foliage/Foliage.h @@ -190,7 +190,7 @@ private: typedef Array> DrawCallsList; typedef Dictionary BatchedDrawCalls; - void DrawInstance(DrawContext& context, FoliageInstance& instance, Model* model, int32 lod, float lodDitherFactor, DrawCallsList* drawCallsLists, BatchedDrawCalls& result) const; + void DrawInstance(DrawContext& context, FoliageInstance& instance, int32 lod, float lodDitherFactor, DrawCallsList* drawCallsLists, BatchedDrawCalls& result) const; void DrawCluster(DrawContext& context, FoliageCluster* cluster, DrawCallsList* drawCallsLists, BatchedDrawCalls& result) const; void DrawType(RenderContext& renderContext, const FoliageType& type, DrawCallsList* drawCallsLists); #else