Optimize Foliage rendering with manual instanced draw calls batching
This commit is contained in:
@@ -81,48 +81,40 @@ void Foliage::AddToCluster(ChunkedArray<FoliageCluster, FOLIAGE_CLUSTER_CHUNKS_S
|
||||
|
||||
#if !FOLIAGE_USE_SINGLE_QUAD_TREE && FOLIAGE_USE_DRAW_CALLS_BATCHING
|
||||
|
||||
void Foliage::DrawInstance(RenderContext& renderContext, FoliageInstance& instance, FoliageType& type, Model* model, const ModelLOD& modelLod, float lodDitherFactor)
|
||||
void Foliage::DrawInstance(RenderContext& renderContext, FoliageInstance& instance, FoliageType& type, Model* model, int32 lod, float lodDitherFactor, DrawCallsList* drawCallsLists, BatchedDrawCalls& result) const
|
||||
{
|
||||
for (const auto& mesh : modelLod.Meshes)
|
||||
const auto& meshes = model->LODs[lod].Meshes;
|
||||
for (int32 meshIndex = 0; meshIndex < meshes.Count(); meshIndex++)
|
||||
{
|
||||
const auto& entry = type.Entries[mesh.GetMaterialSlotIndex()];
|
||||
if (!entry.Visible || !mesh.IsInitialized())
|
||||
return;
|
||||
const MaterialSlot& slot = model->MaterialSlots[mesh.GetMaterialSlotIndex()];
|
||||
const auto shadowsMode = static_cast<ShadowsCastingMode>(entry.ShadowsMode & slot.ShadowsMode);
|
||||
const auto drawModes = static_cast<DrawPass>(type._drawModes & renderContext.View.GetShadowsDrawPassMask(shadowsMode));
|
||||
auto& drawCall = drawCallsLists[lod][meshIndex];
|
||||
if (!drawCall.DrawCall.Material)
|
||||
continue;
|
||||
|
||||
// Select material
|
||||
MaterialBase* material;
|
||||
if (entry.Material && entry.Material->IsLoaded())
|
||||
material = entry.Material;
|
||||
else if (slot.Material && slot.Material->IsLoaded())
|
||||
material = slot.Material;
|
||||
else
|
||||
material = GPUDevice::Instance->GetDefaultMaterial();
|
||||
if (!material || !material->IsSurface() || drawModes == DrawPass::None)
|
||||
return;
|
||||
DrawKey key;
|
||||
key.Mat = drawCall.DrawCall.Material;
|
||||
key.Geo = &meshes[meshIndex];
|
||||
key.Lightmap = instance.Lightmap.TextureIndex;
|
||||
auto* e = result.TryGet(key);
|
||||
if (!e)
|
||||
{
|
||||
e = &result[key];
|
||||
e->DrawCall.Material = key.Mat;
|
||||
e->DrawCall.Surface.Lightmap = _staticFlags & StaticFlags::Lightmap ? _scene->LightmapsData.GetReadyLightmap(key.Lightmap) : nullptr;
|
||||
}
|
||||
|
||||
// Submit draw call
|
||||
DrawCall drawCall;
|
||||
mesh.GetDrawCallGeometry(drawCall);
|
||||
drawCall.InstanceCount = 1;
|
||||
drawCall.Material = material;
|
||||
drawCall.World = instance.World;
|
||||
drawCall.ObjectPosition = drawCall.World.GetTranslation();
|
||||
drawCall.Surface.GeometrySize = mesh.GetBox().GetSize();
|
||||
drawCall.Surface.PrevWorld = instance.World;
|
||||
drawCall.Surface.Lightmap = _staticFlags & StaticFlags::Lightmap ? _scene->LightmapsData.GetReadyLightmap(instance.Lightmap.TextureIndex) : nullptr;
|
||||
drawCall.Surface.LightmapUVsArea = instance.Lightmap.UVsArea;
|
||||
drawCall.Surface.Skinning = nullptr;
|
||||
drawCall.Surface.LODDitherFactor = lodDitherFactor;
|
||||
drawCall.WorldDeterminantSign = 1;
|
||||
drawCall.PerInstanceRandom = instance.Random;
|
||||
renderContext.List->AddDrawCall(drawModes, _staticFlags, drawCall, entry.ReceiveDecals);
|
||||
// Add instance to the draw batch
|
||||
auto& instanceData = e->Instances.AddOne();
|
||||
instanceData.InstanceOrigin = Vector3(instance.World.M41, instance.World.M42, instance.World.M43);
|
||||
instanceData.PerInstanceRandom = instance.Random;
|
||||
instanceData.InstanceTransform1 = Vector3(instance.World.M11, instance.World.M12, instance.World.M13);
|
||||
instanceData.LODDitherFactor = lodDitherFactor;
|
||||
instanceData.InstanceTransform2 = Vector3(instance.World.M21, instance.World.M22, instance.World.M23);
|
||||
instanceData.InstanceTransform3 = Vector3(instance.World.M31, instance.World.M32, instance.World.M33);
|
||||
instanceData.InstanceLightmapArea = Half4(instance.Lightmap.UVsArea);
|
||||
}
|
||||
}
|
||||
|
||||
void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, FoliageType& type)
|
||||
void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, FoliageType& type, DrawCallsList* drawCallsLists, BatchedDrawCalls& result) const
|
||||
{
|
||||
// Skip clusters that around too far from view
|
||||
if (Vector3::Distance(renderContext.View.Position, cluster->TotalBoundsSphere.Center) - cluster->TotalBoundsSphere.Radius > cluster->MaxCullDistance)
|
||||
@@ -138,7 +130,7 @@ void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster,
|
||||
|
||||
#define DRAW_CLUSTER(idx) \
|
||||
if (renderContext.View.CullingFrustum.Intersects(cluster->Children[idx]->TotalBounds)) \
|
||||
DrawCluster(renderContext, cluster->Children[idx], type)
|
||||
DrawCluster(renderContext, cluster->Children[idx], type, drawCallsLists, result)
|
||||
DRAW_CLUSTER(0);
|
||||
DRAW_CLUSTER(1);
|
||||
DRAW_CLUSTER(2);
|
||||
@@ -182,7 +174,7 @@ void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster,
|
||||
{
|
||||
const auto prevLOD = model->ClampLODIndex(instance.DrawState.PrevLOD);
|
||||
const float normalizedProgress = static_cast<float>(instance.DrawState.LODTransition) * (1.0f / 255.0f);
|
||||
DrawInstance(renderContext, instance, type, model, model->LODs[prevLOD], normalizedProgress);
|
||||
DrawInstance(renderContext, instance, type, model, prevLOD, normalizedProgress, drawCallsLists, result);
|
||||
}
|
||||
}
|
||||
instance.DrawState.PrevFrame = frame;
|
||||
@@ -219,19 +211,19 @@ void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster,
|
||||
// Draw
|
||||
if (instance.DrawState.PrevLOD == lodIndex)
|
||||
{
|
||||
DrawInstance(renderContext, instance, type, model, model->LODs[lodIndex], 0.0f);
|
||||
DrawInstance(renderContext, instance, type, model, lodIndex, 0.0f, drawCallsLists, result);
|
||||
}
|
||||
else if (instance.DrawState.PrevLOD == -1)
|
||||
{
|
||||
const float normalizedProgress = static_cast<float>(instance.DrawState.LODTransition) * (1.0f / 255.0f);
|
||||
DrawInstance(renderContext, instance, type, model, model->LODs[lodIndex], 1.0f - normalizedProgress);
|
||||
DrawInstance(renderContext, instance, type, model, lodIndex, 1.0f - normalizedProgress, drawCallsLists, result);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto prevLOD = model->ClampLODIndex(instance.DrawState.PrevLOD);
|
||||
const float normalizedProgress = static_cast<float>(instance.DrawState.LODTransition) * (1.0f / 255.0f);
|
||||
DrawInstance(renderContext, instance, type, model, model->LODs[prevLOD], normalizedProgress);
|
||||
DrawInstance(renderContext, instance, type, model, model->LODs[lodIndex], normalizedProgress - 1.0f);
|
||||
DrawInstance(renderContext, instance, type, model, prevLOD, normalizedProgress, drawCallsLists, result);
|
||||
DrawInstance(renderContext, instance, type, model, lodIndex, normalizedProgress - 1.0f, drawCallsLists, result);
|
||||
}
|
||||
|
||||
//DebugDraw::DrawSphere(instance.Bounds, Color::YellowGreen);
|
||||
@@ -878,13 +870,17 @@ void Foliage::Draw(RenderContext& renderContext)
|
||||
}
|
||||
|
||||
// Draw visible clusters
|
||||
#if FOLIAGE_USE_SINGLE_QUAD_TREE
|
||||
#if FOLIAGE_USE_SINGLE_QUAD_TREE || !FOLIAGE_USE_DRAW_CALLS_BATCHING
|
||||
Mesh::DrawInfo draw;
|
||||
draw.Flags = GetStaticFlags();
|
||||
draw.DrawModes = (DrawPass)(DrawPass::Default & view.Pass);
|
||||
draw.LODBias = 0;
|
||||
draw.ForcedLOD = -1;
|
||||
draw.VertexColors = nullptr;
|
||||
#else
|
||||
DrawCallsList drawCallsLists[MODEL_MAX_LODS];
|
||||
#endif
|
||||
#if FOLIAGE_USE_SINGLE_QUAD_TREE
|
||||
if (Root)
|
||||
DrawCluster(renderContext, Root, draw);
|
||||
#else
|
||||
@@ -892,7 +888,112 @@ void Foliage::Draw(RenderContext& renderContext)
|
||||
{
|
||||
if (type.Root && type._canDraw && type.Model->CanBeRendered())
|
||||
{
|
||||
DrawCluster(renderContext, type.Root, type);
|
||||
#if FOLIAGE_USE_DRAW_CALLS_BATCHING
|
||||
// Initialize draw calls for foliage type all LODs meshes
|
||||
for (int32 lod = 0; lod < type.Model->LODs.Count(); lod++)
|
||||
{
|
||||
auto& modelLod = type.Model->LODs[lod];
|
||||
DrawCallsList& drawCallsList = drawCallsLists[lod];
|
||||
const auto& meshes = modelLod.Meshes;
|
||||
drawCallsList.Resize(meshes.Count());
|
||||
for (int32 meshIndex = 0; meshIndex < meshes.Count(); meshIndex++)
|
||||
{
|
||||
const auto& mesh = meshes[meshIndex];
|
||||
auto& drawCall = drawCallsList[meshIndex];
|
||||
drawCall.DrawCall.Material = nullptr;
|
||||
|
||||
// Check entry visibility
|
||||
const auto& entry = type.Entries[mesh.GetMaterialSlotIndex()];
|
||||
if (!entry.Visible || !mesh.IsInitialized())
|
||||
continue;
|
||||
const MaterialSlot& slot = type.Model->MaterialSlots[mesh.GetMaterialSlotIndex()];
|
||||
|
||||
// Select material
|
||||
MaterialBase* material;
|
||||
if (entry.Material && entry.Material->IsLoaded())
|
||||
material = entry.Material;
|
||||
else if (slot.Material && slot.Material->IsLoaded())
|
||||
material = slot.Material;
|
||||
else
|
||||
material = GPUDevice::Instance->GetDefaultMaterial();
|
||||
if (!material || !material->IsSurface())
|
||||
continue;
|
||||
|
||||
// Select draw modes
|
||||
const auto shadowsMode = static_cast<ShadowsCastingMode>(entry.ShadowsMode & slot.ShadowsMode);
|
||||
const auto drawModes = static_cast<DrawPass>(type._drawModes & renderContext.View.GetShadowsDrawPassMask(shadowsMode)) & material->GetDrawModes();
|
||||
if (drawModes == 0)
|
||||
continue;
|
||||
|
||||
drawCall.DrawCall.Material = material;
|
||||
}
|
||||
}
|
||||
|
||||
// Draw instances of the foliage type
|
||||
BatchedDrawCalls result;
|
||||
DrawCluster(renderContext, type.Root, type, drawCallsLists, result);
|
||||
|
||||
// Submit draw calls with valid instances added
|
||||
for (auto& e : result)
|
||||
{
|
||||
auto& drawCall = e.Value;
|
||||
if (drawCall.Instances.IsEmpty())
|
||||
continue;
|
||||
const auto& mesh = *e.Key.Geo;
|
||||
const auto& entry = type.Entries[mesh.GetMaterialSlotIndex()];
|
||||
const MaterialSlot& slot = type.Model->MaterialSlots[mesh.GetMaterialSlotIndex()];
|
||||
const auto shadowsMode = static_cast<ShadowsCastingMode>(entry.ShadowsMode & slot.ShadowsMode);
|
||||
const auto drawModes = (DrawPass)(static_cast<DrawPass>(type._drawModes & renderContext.View.GetShadowsDrawPassMask(shadowsMode)) & drawCall.DrawCall.Material->GetDrawModes());
|
||||
|
||||
// Setup draw call
|
||||
mesh.GetDrawCallGeometry(drawCall.DrawCall);
|
||||
drawCall.DrawCall.InstanceCount = 1;
|
||||
auto& firstInstance = drawCall.Instances[0];
|
||||
drawCall.DrawCall.ObjectPosition = firstInstance.InstanceOrigin;
|
||||
drawCall.DrawCall.PerInstanceRandom = firstInstance.PerInstanceRandom;
|
||||
auto lightmapArea = firstInstance.InstanceLightmapArea.ToVector4();
|
||||
drawCall.DrawCall.Surface.LightmapUVsArea = *(Rectangle*)&lightmapArea;
|
||||
drawCall.DrawCall.Surface.LODDitherFactor = firstInstance.LODDitherFactor;
|
||||
drawCall.DrawCall.World.SetRow1(Vector4(firstInstance.InstanceTransform1, 0.0f));
|
||||
drawCall.DrawCall.World.SetRow2(Vector4(firstInstance.InstanceTransform2, 0.0f));
|
||||
drawCall.DrawCall.World.SetRow3(Vector4(firstInstance.InstanceTransform3, 0.0f));
|
||||
drawCall.DrawCall.World.SetRow4(Vector4(firstInstance.InstanceOrigin, 1.0f));
|
||||
drawCall.DrawCall.Surface.PrevWorld = drawCall.DrawCall.World;
|
||||
drawCall.DrawCall.Surface.GeometrySize = mesh.GetBox().GetSize();
|
||||
drawCall.DrawCall.Surface.Skinning = nullptr;
|
||||
drawCall.DrawCall.WorldDeterminantSign = 1;
|
||||
|
||||
const int32 batchIndex = renderContext.List->BatchedDrawCalls.Count();
|
||||
renderContext.List->BatchedDrawCalls.Add(MoveTemp(drawCall));
|
||||
|
||||
// Add draw call to proper draw lists
|
||||
if (drawModes & DrawPass::Depth)
|
||||
{
|
||||
renderContext.List->DrawCallsLists[(int32)DrawCallsListType::Depth].PreBatchedDrawCalls.Add(batchIndex);
|
||||
}
|
||||
if (drawModes & DrawPass::GBuffer)
|
||||
{
|
||||
if (entry.ReceiveDecals)
|
||||
renderContext.List->DrawCallsLists[(int32)DrawCallsListType::GBuffer].PreBatchedDrawCalls.Add(batchIndex);
|
||||
else
|
||||
renderContext.List->DrawCallsLists[(int32)DrawCallsListType::GBufferNoDecals].PreBatchedDrawCalls.Add(batchIndex);
|
||||
}
|
||||
if (drawModes & DrawPass::Forward)
|
||||
{
|
||||
renderContext.List->DrawCallsLists[(int32)DrawCallsListType::Forward].PreBatchedDrawCalls.Add(batchIndex);
|
||||
}
|
||||
if (drawModes & DrawPass::Distortion)
|
||||
{
|
||||
renderContext.List->DrawCallsLists[(int32)DrawCallsListType::Distortion].PreBatchedDrawCalls.Add(batchIndex);
|
||||
}
|
||||
if (drawModes & DrawPass::MotionVectors && (_staticFlags & StaticFlags::Transform) == 0)
|
||||
{
|
||||
renderContext.List->DrawCallsLists[(int32)DrawCallsListType::MotionVectors].PreBatchedDrawCalls.Add(batchIndex);
|
||||
}
|
||||
}
|
||||
#else
|
||||
DrawCluster(renderContext, type.Root, draw);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -153,8 +153,30 @@ private:
|
||||
|
||||
void AddToCluster(ChunkedArray<FoliageCluster, FOLIAGE_CLUSTER_CHUNKS_SIZE>& clusters, FoliageCluster* cluster, FoliageInstance& instance);
|
||||
#if !FOLIAGE_USE_SINGLE_QUAD_TREE && FOLIAGE_USE_DRAW_CALLS_BATCHING
|
||||
void DrawInstance(RenderContext& renderContext, FoliageInstance& instance, FoliageType& type, Model* model, const ModelLOD& modelLod, float lodDitherFactor);
|
||||
void DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, FoliageType& type);
|
||||
struct DrawKey
|
||||
{
|
||||
IMaterial* Mat;
|
||||
const Mesh* Geo;
|
||||
int32 Lightmap;
|
||||
|
||||
friend bool operator==(const DrawKey& lhs, const DrawKey& rhs)
|
||||
{
|
||||
return lhs.Mat == rhs.Mat && lhs.Geo == rhs.Geo && lhs.Lightmap == rhs.Lightmap;
|
||||
}
|
||||
|
||||
friend uint32 GetHash(const DrawKey& key)
|
||||
{
|
||||
uint32 hash = (uint32)((int64)(key.Mat) >> 3);
|
||||
hash ^= (uint32)((int64)(key.Geo) >> 3) + 0x9e3779b9 + (hash << 6) + (hash >> 2);
|
||||
hash ^= (uint32)key.Lightmap;
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
typedef Array<struct BatchedDrawCall, InlinedAllocation<8>> DrawCallsList;
|
||||
typedef Dictionary<DrawKey, struct BatchedDrawCall> BatchedDrawCalls;
|
||||
void DrawInstance(RenderContext& renderContext, FoliageInstance& instance, FoliageType& type, Model* model, int32 lod, float lodDitherFactor, DrawCallsList* drawCallsLists, BatchedDrawCalls& result) const;
|
||||
void DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, FoliageType& type, DrawCallsList* drawCallsLists, BatchedDrawCalls& result) const;
|
||||
#else
|
||||
void DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, Mesh::DrawInfo& draw);
|
||||
#endif
|
||||
|
||||
@@ -316,6 +316,19 @@ bool RenderList::HasAnyPostAA(RenderContext& renderContext) const
|
||||
return false;
|
||||
}
|
||||
|
||||
void DrawCallsList::Clear()
|
||||
{
|
||||
Indices.Clear();
|
||||
PreBatchedDrawCalls.Clear();
|
||||
Batches.Clear();
|
||||
CanUseInstancing = true;
|
||||
}
|
||||
|
||||
bool DrawCallsList::IsEmpty() const
|
||||
{
|
||||
return Indices.Count() + PreBatchedDrawCalls.Count() == 0;
|
||||
}
|
||||
|
||||
RenderList::RenderList(const SpawnParams& params)
|
||||
: PersistentScriptingObject(params)
|
||||
, DirectionalLights(4)
|
||||
@@ -341,6 +354,7 @@ void RenderList::Init(RenderContext& renderContext)
|
||||
void RenderList::Clear()
|
||||
{
|
||||
DrawCalls.Clear();
|
||||
BatchedDrawCalls.Clear();
|
||||
for (auto& list : DrawCallsLists)
|
||||
list.Clear();
|
||||
PointLights.Clear();
|
||||
@@ -502,13 +516,9 @@ bool CanUseInstancing(DrawPass pass)
|
||||
|
||||
void RenderList::ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsList& list)
|
||||
{
|
||||
// Skip if no rendering to perform
|
||||
if (list.Batches.IsEmpty())
|
||||
if (list.IsEmpty())
|
||||
return;
|
||||
|
||||
PROFILE_GPU_CPU("Drawing");
|
||||
|
||||
const int32 batchesSize = list.Batches.Count();
|
||||
const auto context = GPUDevice::Instance->GetMainContext();
|
||||
bool useInstancing = list.CanUseInstancing && CanUseInstancing(renderContext.View.Pass) && GPUDevice::Instance->Limits.HasInstancing;
|
||||
|
||||
@@ -520,13 +530,17 @@ void RenderList::ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsL
|
||||
{
|
||||
// Prepare buffer memory
|
||||
int32 batchesCount = 0;
|
||||
for (int32 i = 0; i < batchesSize; i++)
|
||||
for (int32 i = 0; i < list.Batches.Count(); i++)
|
||||
{
|
||||
auto& batch = list.Batches[i];
|
||||
if (batch.BatchSize > 1)
|
||||
{
|
||||
batchesCount += batch.BatchSize;
|
||||
}
|
||||
}
|
||||
for (int32 i = 0; i < list.PreBatchedDrawCalls.Count(); i++)
|
||||
{
|
||||
auto& batch = BatchedDrawCalls[list.PreBatchedDrawCalls[i]];
|
||||
if (batch.Instances.Count() > 1)
|
||||
batchesCount += batch.Instances.Count();
|
||||
}
|
||||
if (batchesCount == 0)
|
||||
{
|
||||
@@ -539,7 +553,7 @@ void RenderList::ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsL
|
||||
auto instanceData = (InstanceData*)_instanceBuffer.Data.Get();
|
||||
|
||||
// Write to instance buffer
|
||||
for (int32 i = 0; i < batchesSize; i++)
|
||||
for (int32 i = 0; i < list.Batches.Count(); i++)
|
||||
{
|
||||
auto& batch = list.Batches[i];
|
||||
if (batch.BatchSize > 1)
|
||||
@@ -554,6 +568,15 @@ void RenderList::ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsL
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int32 i = 0; i < list.PreBatchedDrawCalls.Count(); i++)
|
||||
{
|
||||
auto& batch = BatchedDrawCalls[list.PreBatchedDrawCalls[i]];
|
||||
if (batch.Instances.Count() > 1)
|
||||
{
|
||||
Platform::MemoryCopy(instanceData, batch.Instances.Get(), batch.Instances.Count() * sizeof(InstanceData));
|
||||
instanceData += batch.Instances.Count();
|
||||
}
|
||||
}
|
||||
|
||||
// Upload data
|
||||
_instanceBuffer.Flush(context);
|
||||
@@ -568,7 +591,7 @@ DRAW:
|
||||
int32 instanceBufferOffset = 0;
|
||||
GPUBuffer* vb[4];
|
||||
uint32 vbOffsets[4];
|
||||
for (int32 i = 0; i < batchesSize; i++)
|
||||
for (int32 i = 0; i < list.Batches.Count(); i++)
|
||||
{
|
||||
auto& batch = list.Batches[i];
|
||||
auto& drawCall = DrawCalls[list.Indices[batch.StartIndex]];
|
||||
@@ -615,16 +638,64 @@ DRAW:
|
||||
vbCount++;
|
||||
context->BindVB(ToSpan(vb, vbCount), vbOffsets);
|
||||
context->DrawIndexedInstanced(drawCall.Draw.IndicesCount, batch.InstanceCount, instanceBufferOffset, 0, drawCall.Draw.StartIndex);
|
||||
|
||||
instanceBufferOffset += batch.BatchSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int32 i = 0; i < list.PreBatchedDrawCalls.Count(); i++)
|
||||
{
|
||||
auto& batch = BatchedDrawCalls[list.PreBatchedDrawCalls[i]];
|
||||
auto& drawCall = batch.DrawCall;
|
||||
|
||||
int32 vbCount = 0;
|
||||
while (drawCall.Geometry.VertexBuffers[vbCount] && vbCount < ARRAY_COUNT(drawCall.Geometry.VertexBuffers))
|
||||
{
|
||||
vb[vbCount] = drawCall.Geometry.VertexBuffers[vbCount];
|
||||
vbOffsets[vbCount] = drawCall.Geometry.VertexBuffersOffsets[vbCount];
|
||||
vbCount++;
|
||||
}
|
||||
for (int32 j = vbCount; j < ARRAY_COUNT(drawCall.Geometry.VertexBuffers); j++)
|
||||
{
|
||||
vb[vbCount] = nullptr;
|
||||
vbOffsets[vbCount] = 0;
|
||||
}
|
||||
|
||||
bindParams.FirstDrawCall = &drawCall;
|
||||
bindParams.DrawCallsCount = batch.Instances.Count();
|
||||
drawCall.Material->Bind(bindParams);
|
||||
|
||||
context->BindIB(drawCall.Geometry.IndexBuffer);
|
||||
|
||||
if (drawCall.InstanceCount == 0)
|
||||
{
|
||||
ASSERT_LOW_LAYER(batch.Instances.Count() == 1);
|
||||
context->BindVB(ToSpan(vb, vbCount), vbOffsets);
|
||||
context->DrawIndexedInstancedIndirect(drawCall.Draw.IndirectArgsBuffer, drawCall.Draw.IndirectArgsOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (batch.Instances.Count() == 1)
|
||||
{
|
||||
context->BindVB(ToSpan(vb, vbCount), vbOffsets);
|
||||
context->DrawIndexedInstanced(drawCall.Draw.IndicesCount, batch.Instances.Count(), 0, 0, drawCall.Draw.StartIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
vbCount = 3;
|
||||
vb[vbCount] = _instanceBuffer.GetBuffer();
|
||||
vbOffsets[vbCount] = 0;
|
||||
vbCount++;
|
||||
context->BindVB(ToSpan(vb, vbCount), vbOffsets);
|
||||
context->DrawIndexedInstanced(drawCall.Draw.IndicesCount, batch.Instances.Count(), instanceBufferOffset, 0, drawCall.Draw.StartIndex);
|
||||
instanceBufferOffset += batch.Instances.Count();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bindParams.DrawCallsCount = 1;
|
||||
for (int32 i = 0; i < batchesSize; i++)
|
||||
for (int32 i = 0; i < list.Batches.Count(); i++)
|
||||
{
|
||||
auto& batch = list.Batches[i];
|
||||
|
||||
@@ -647,6 +718,31 @@ DRAW:
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int32 i = 0; i < list.PreBatchedDrawCalls.Count(); i++)
|
||||
{
|
||||
auto& batch = BatchedDrawCalls[list.PreBatchedDrawCalls[i]];
|
||||
auto drawCall = batch.DrawCall;
|
||||
bindParams.FirstDrawCall = &drawCall;
|
||||
|
||||
for (int32 j = 0; j < batch.Instances.Count(); j++)
|
||||
{
|
||||
auto& instance = batch.Instances[j];
|
||||
drawCall.ObjectPosition = instance.InstanceOrigin;
|
||||
drawCall.PerInstanceRandom = instance.PerInstanceRandom;
|
||||
auto lightmapArea = instance.InstanceLightmapArea.ToVector4();
|
||||
drawCall.Surface.LightmapUVsArea = *(Rectangle*)&lightmapArea;
|
||||
drawCall.Surface.LODDitherFactor = instance.LODDitherFactor;
|
||||
drawCall.World.SetRow1(Vector4(instance.InstanceTransform1, 0.0f));
|
||||
drawCall.World.SetRow2(Vector4(instance.InstanceTransform2, 0.0f));
|
||||
drawCall.World.SetRow3(Vector4(instance.InstanceTransform3, 0.0f));
|
||||
drawCall.World.SetRow4(Vector4(instance.InstanceOrigin, 1.0f));
|
||||
drawCall.Material->Bind(bindParams);
|
||||
|
||||
context->BindIB(drawCall.Geometry.IndexBuffer);
|
||||
context->BindVB(ToSpan(drawCall.Geometry.VertexBuffers, 3), drawCall.Geometry.VertexBuffersOffsets);
|
||||
context->DrawIndexedInstanced(drawCall.Draw.IndicesCount, drawCall.InstanceCount, 0, 0, drawCall.Draw.StartIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -203,6 +203,12 @@ struct DrawBatch
|
||||
}
|
||||
};
|
||||
|
||||
struct BatchedDrawCall
|
||||
{
|
||||
DrawCall DrawCall;
|
||||
Array<struct InstanceData> Instances;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Represents a list of draw calls.
|
||||
/// </summary>
|
||||
@@ -213,6 +219,11 @@ struct DrawCallsList
|
||||
/// </summary>
|
||||
Array<int32> Indices;
|
||||
|
||||
/// <summary>
|
||||
/// The list of external draw calls indices to render.
|
||||
/// </summary>
|
||||
Array<int32> PreBatchedDrawCalls;
|
||||
|
||||
/// <summary>
|
||||
/// The draw calls batches (for instancing).
|
||||
/// </summary>
|
||||
@@ -223,17 +234,8 @@ struct DrawCallsList
|
||||
/// </summary>
|
||||
bool CanUseInstancing;
|
||||
|
||||
void Clear()
|
||||
{
|
||||
Indices.Clear();
|
||||
Batches.Clear();
|
||||
CanUseInstancing = true;
|
||||
}
|
||||
|
||||
bool IsEmpty() const
|
||||
{
|
||||
return Indices.IsEmpty();
|
||||
}
|
||||
void Clear();
|
||||
bool IsEmpty() const;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@@ -242,7 +244,6 @@ struct DrawCallsList
|
||||
API_CLASS(Sealed) class FLAXENGINE_API RenderList : public PersistentScriptingObject
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE(RenderList);
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Allocates the new renderer list object or reuses already allocated one.
|
||||
@@ -268,6 +269,11 @@ public:
|
||||
/// </summary>
|
||||
Array<DrawCall> DrawCalls;
|
||||
|
||||
/// <summary>
|
||||
/// Draw calls list with pre-batched instances (for all draw passes).
|
||||
/// </summary>
|
||||
Array<BatchedDrawCall> BatchedDrawCalls;
|
||||
|
||||
/// <summary>
|
||||
/// The draw calls lists. Each for the separate draw pass.
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user