Optimize foliage rendering with less cache misses

This commit is contained in:
2026-04-28 00:24:05 +02:00
parent bf9a015959
commit f4905cfccc
2 changed files with 19 additions and 19 deletions
+18 -18
View File
@@ -130,26 +130,25 @@ void Foliage::AddToCluster(ChunkedArray<FoliageCluster, FOLIAGE_CLUSTER_CHUNKS_S
#if !FOLIAGE_USE_SINGLE_QUAD_TREE && FOLIAGE_USE_DRAW_CALLS_BATCHING #if !FOLIAGE_USE_SINGLE_QUAD_TREE && FOLIAGE_USE_DRAW_CALLS_BATCHING
void Foliage::DrawInstance(DrawContext& context, FoliageInstance& instance, Model* model, int32 lod, float lodDitherFactor, DrawCallsList* drawCallsLists, BatchedDrawCalls& result) const void Foliage::DrawInstance(DrawContext& context, FoliageInstance& instance, int32 lod, float lodDitherFactor, DrawCallsList* drawCallsLists, BatchedDrawCalls& result) const
{ {
const auto& meshes = model->LODs.Get()[lod].Meshes; const auto& drawCalls = drawCallsLists[lod];
for (int32 meshIndex = 0; meshIndex < meshes.Count(); meshIndex++) const auto* drawCallsPtr = drawCalls.Get();
for (int32 i = 0; i < drawCalls.Count(); i++)
{ {
auto& drawCall = drawCallsLists[lod][meshIndex]; auto& drawCall = drawCallsPtr[i];
if (!drawCall.Material)
continue;
DrawKey key; DrawKey key;
key.Mat = drawCall.Material; 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; key.Lightmap = instance.LightmapTextureIndex;
auto* e = result.TryGet(key); auto* e = result.TryGet(key);
if (!e) if (!e)
{ {
e = &result.Add(key, BatchedDrawCall(context.RenderContext.List))->Value; e = &result.Add(key, BatchedDrawCall(context.RenderContext.List))->Value;
e->DrawCall.Material = key.Mat; e->DrawCall.Material = key.Mat;
e->DrawCall.Surface.Lightmap = EnumHasAnyFlags(_staticFlags, StaticFlags::Lightmap) && _scene ? _scene->LightmapsData.GetReadyLightmap(key.Lightmap) : nullptr; e->DrawCall.Surface.Lightmap = key.Lightmap != -1 && EnumHasAnyFlags(_staticFlags, StaticFlags::Lightmap) && _scene ? _scene->LightmapsData.GetReadyLightmap(key.Lightmap) : nullptr;
e->DrawCall.Surface.GeometrySize = key.Geo->GetBox().GetSize(); e->DrawCall.Surface.GeometrySize = drawCall.Surface.GeometrySize;
} }
// Add instance to the draw batch // 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 auto prevLOD = model->ClampLODIndex(instance.DrawStatePrevLOD);
const float normalizedProgress = static_cast<float>(instance.DrawStateLODTransition) * (1.0f / 255.0f); const float normalizedProgress = static_cast<float>(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) else if (instance.DrawStateLODTransition < 255)
{ {
const auto prevLOD = model->ClampLODIndex(instance.DrawStatePrevLOD); const auto prevLOD = model->ClampLODIndex(instance.DrawStatePrevLOD);
const float normalizedProgress = static_cast<float>(instance.DrawStateLODTransition) * (1.0f / 255.0f); const float normalizedProgress = static_cast<float>(instance.DrawStateLODTransition) * (1.0f / 255.0f);
DrawInstance(context, instance, model, prevLOD, normalizedProgress, drawCallsLists, result); DrawInstance(context, instance, prevLOD, normalizedProgress, drawCallsLists, result);
} }
} }
instance.DrawStatePrevFrame = frame; instance.DrawStatePrevFrame = frame;
@@ -278,19 +277,19 @@ void Foliage::DrawCluster(DrawContext& context, FoliageCluster* cluster, DrawCal
// Draw // Draw
if (instance.DrawStatePrevLOD == lodIndex) 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) else if (instance.DrawStatePrevLOD == -1)
{ {
const float normalizedProgress = static_cast<float>(instance.DrawStateLODTransition) * (1.0f / 255.0f); const float normalizedProgress = static_cast<float>(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 else
{ {
const auto prevLOD = model->ClampLODIndex(instance.DrawStatePrevLOD); const auto prevLOD = model->ClampLODIndex(instance.DrawStatePrevLOD);
const float normalizedProgress = static_cast<float>(instance.DrawStateLODTransition) * (1.0f / 255.0f); const float normalizedProgress = static_cast<float>(instance.DrawStateLODTransition) * (1.0f / 255.0f);
DrawInstance(context, instance, model, prevLOD, normalizedProgress, drawCallsLists, result); DrawInstance(context, instance, prevLOD, normalizedProgress, drawCallsLists, result);
DrawInstance(context, instance, model, lodIndex, normalizedProgress - 1.0f, drawCallsLists, result); DrawInstance(context, instance, lodIndex, normalizedProgress - 1.0f, drawCallsLists, result);
} }
//DebugDraw::DrawSphere(instance.Bounds, Color::YellowGreen); //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]; auto& modelLod = type.Model->LODs[lod];
DrawCallsList& drawCallsList = drawCallsLists[lod]; DrawCallsList& drawCallsList = drawCallsLists[lod];
const auto& meshes = modelLod.Meshes; const auto& meshes = modelLod.Meshes;
drawCallsList.Resize(meshes.Count()); drawCallsList.EnsureCapacity(meshes.Count());
for (int32 meshIndex = 0; meshIndex < meshes.Count(); meshIndex++) for (int32 meshIndex = 0; meshIndex < meshes.Count(); meshIndex++)
{ {
const auto& mesh = meshes.Get()[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 // Check entry visibility
const auto& entry = type.Entries[mesh.GetMaterialSlotIndex()]; const auto& entry = type.Entries[mesh.GetMaterialSlotIndex()];
@@ -523,7 +520,10 @@ void Foliage::DrawType(RenderContext& renderContext, const FoliageType& type, Me
if (drawModes == DrawPass::None) if (drawModes == DrawPass::None)
continue; continue;
// Add mesh to drawing
auto& drawCall = drawCallsList.AddOne();
drawCall.Material = material; drawCall.Material = material;
drawCall.Geometry.IndexBuffer = (GPUBuffer*)(void*)&mesh; // Wrap pointer to mesh for DrawKey::Geo
drawCall.Surface.GeometrySize = mesh.GetBox().GetSize(); drawCall.Surface.GeometrySize = mesh.GetBox().GetSize();
} }
} }
+1 -1
View File
@@ -190,7 +190,7 @@ private:
typedef Array<struct DrawCall, InlinedAllocation<8>> DrawCallsList; typedef Array<struct DrawCall, InlinedAllocation<8>> DrawCallsList;
typedef Dictionary<DrawKey, struct BatchedDrawCall, ConcurrentArenaAllocation> BatchedDrawCalls; typedef Dictionary<DrawKey, struct BatchedDrawCall, ConcurrentArenaAllocation> 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 DrawCluster(DrawContext& context, FoliageCluster* cluster, DrawCallsList* drawCallsLists, BatchedDrawCalls& result) const;
void DrawType(RenderContext& renderContext, const FoliageType& type, DrawCallsList* drawCallsLists); void DrawType(RenderContext& renderContext, const FoliageType& type, DrawCallsList* drawCallsLists);
#else #else