Refactor draw calls and instancing logic to be more modular

This commit is contained in:
Wojtek Figat
2021-02-03 09:33:48 +01:00
parent 81cc8cf69c
commit a560b19cbc
23 changed files with 255 additions and 268 deletions

View File

@@ -19,6 +19,11 @@
#define BATCH_KEY_BITS 32
#define BATCH_KEY_MASK ((1 << BATCH_KEY_BITS) - 1)
static_assert(sizeof(DrawCall) <= 280, "Too big draw call data size.");
static_assert(sizeof(DrawCall::Surface) >= sizeof(DrawCall::Terrain), "Wrong draw call data size.");
static_assert(sizeof(DrawCall::Surface) >= sizeof(DrawCall::Particle), "Wrong draw call data size.");
static_assert(sizeof(DrawCall::Surface) >= sizeof(DrawCall::Custom), "Wrong draw call data size.");
namespace
{
// Cached data for the draw calls sorting
@@ -495,24 +500,24 @@ end:
}
}
/// <summary>
/// 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)
namespace
{
return Platform::MemoryCompare(&a.Geometry, &b.Geometry, sizeof(a.Geometry)) == 0 &&
a.Material == b.Material &&
a.Lightmap == b.Lightmap &&
// TODO: add batch.CanBatch flag computed in AddDrawCall to remove those checks here for Skinning and IndirectDrawArgs
a.Skinning == nullptr &&
b.Skinning == nullptr &&
a.IndirectArgsBuffer == nullptr &&
b.IndirectArgsBuffer == nullptr &&
a.WorldDeterminantSign == b.WorldDeterminantSign &&
a.Material->CanUseInstancing();
/// <summary>
/// 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;
return a.Material == b.Material &&
a.Material->CanUseInstancing(handler) &&
Platform::MemoryCompare(&a.Geometry, &b.Geometry, sizeof(a.Geometry)) == 0 &&
a.IndirectArgsBuffer == nullptr &&
b.IndirectArgsBuffer == nullptr &&
a.WorldDeterminantSign == b.WorldDeterminantSign;
}
}
void RenderList::SortDrawCalls(const RenderContext& renderContext, bool reverseDistance, DrawCallsList& list)
@@ -540,7 +545,9 @@ void RenderList::SortDrawCalls(const RenderContext& renderContext, bool reverseD
batchKey = (batchKey * 397) ^ GetHash(drawCall.Geometry.VertexBuffers[1]);
batchKey = (batchKey * 397) ^ GetHash(drawCall.Geometry.VertexBuffers[2]);
batchKey = (batchKey * 397) ^ GetHash(drawCall.Material);
batchKey = (batchKey * 397) ^ GetHash(drawCall.Lightmap);
IMaterial::InstancingHandler handler;
if (drawCall.Material->CanUseInstancing(handler))
handler.GetHash(drawCall, batchKey);
batchKey += (int32)(471 * drawCall.WorldDeterminantSign);
#if USE_BATCH_KEY_MASK
const uint32 batchHashKey = (uint32)batchKey & BATCH_KEY_MASK;
@@ -635,14 +642,12 @@ void RenderList::ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsL
auto& batch = list.Batches[i];
if (batch.BatchSize > 1)
{
IMaterial::InstancingHandler handler;
DrawCalls[list.Indices[batch.StartIndex]].Material->CanUseInstancing(handler);
for (int32 j = 0; j < batch.BatchSize; j++)
{
auto& drawCall = DrawCalls[list.Indices[batch.StartIndex + j]];
instanceData->InstanceOrigin = Vector4(drawCall.World.M41, drawCall.World.M42, drawCall.World.M43, drawCall.PerInstanceRandom);
instanceData->InstanceTransform1 = Vector4(drawCall.World.M11, drawCall.World.M12, drawCall.World.M13, drawCall.LODDitherFactor);
instanceData->InstanceTransform2 = Vector3(drawCall.World.M21, drawCall.World.M22, drawCall.World.M23);
instanceData->InstanceTransform3 = Vector3(drawCall.World.M31, drawCall.World.M32, drawCall.World.M33);
instanceData->InstanceLightmapArea = Half4(drawCall.LightmapUVsArea);
handler.WriteDrawCall(instanceData, drawCall);
instanceData++;
}
}
@@ -742,3 +747,26 @@ DRAW:
}
}
}
void SurfaceDrawCallHandler::GetHash(const DrawCall& drawCall, int32& batchKey)
{
batchKey = (batchKey * 397) ^ ::GetHash(drawCall.Surface.Lightmap);
}
bool SurfaceDrawCallHandler::CanBatch(const DrawCall& a, const DrawCall& b)
{
return a.Surface.Lightmap == b.Surface.Lightmap &&
a.Surface.Skinning == nullptr &&
b.Surface.Skinning == nullptr;
}
void SurfaceDrawCallHandler::WriteDrawCall(InstanceData* instanceData, const DrawCall& drawCall)
{
instanceData->InstanceOrigin = Vector3(drawCall.World.M41, drawCall.World.M42, drawCall.World.M43);
instanceData->PerInstanceRandom = drawCall.PerInstanceRandom;
instanceData->InstanceTransform1 = Vector3(drawCall.World.M11, drawCall.World.M12, drawCall.World.M13);
instanceData->LODDitherFactor = drawCall.Surface.LODDitherFactor;
instanceData->InstanceTransform2 = Vector3(drawCall.World.M21, drawCall.World.M22, drawCall.World.M23);
instanceData->InstanceTransform3 = Vector3(drawCall.World.M31, drawCall.World.M32, drawCall.World.M33);
instanceData->InstanceLightmapArea = Half4(drawCall.Surface.LightmapUVsArea);
}