Refactor SortOrder to use int8 instead of int16 due to performance reasons (more efficent sort keys packing in rendering)

This commit is contained in:
Wojtek Figat
2024-06-29 13:54:02 +02:00
parent 516ed3e9a0
commit 78f3248ac9
15 changed files with 51 additions and 49 deletions

View File

@@ -186,7 +186,7 @@ BoundingBox Model::GetBox(int32 lodIndex) const
return LODs[lodIndex].GetBox();
}
void Model::Draw(const RenderContext& renderContext, MaterialBase* material, const Matrix& world, StaticFlags flags, bool receiveDecals, int16 sortOrder) const
void Model::Draw(const RenderContext& renderContext, MaterialBase* material, const Matrix& world, StaticFlags flags, bool receiveDecals, int8 sortOrder) const
{
if (!CanBeRendered())
return;

View File

@@ -182,7 +182,7 @@ public:
/// <param name="flags">The object static flags.</param>
/// <param name="receiveDecals">True if rendered geometry can receive decals, otherwise false.</param>
/// <param name="sortOrder">Object sorting key.</param>
API_FUNCTION() void Draw(API_PARAM(Ref) const RenderContext& renderContext, MaterialBase* material, API_PARAM(Ref) const Matrix& world, StaticFlags flags = StaticFlags::None, bool receiveDecals = true, int16 sortOrder = 0) const;
API_FUNCTION() void Draw(API_PARAM(Ref) const RenderContext& renderContext, MaterialBase* material, API_PARAM(Ref) const Matrix& world, StaticFlags flags = StaticFlags::None, bool receiveDecals = true, int8 sortOrder = 0) const;
/// <summary>
/// Draws the model.

View File

@@ -385,7 +385,7 @@ void Mesh::Render(GPUContext* context) const
context->DrawIndexedInstanced(_triangles * 3, 1, 0, 0, 0);
}
void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, const Matrix& world, StaticFlags flags, bool receiveDecals, DrawPass drawModes, float perInstanceRandom, int16 sortOrder) const
void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, const Matrix& world, StaticFlags flags, bool receiveDecals, DrawPass drawModes, float perInstanceRandom, int8 sortOrder) const
{
if (!material || !material->IsSurface() || !IsInitialized())
return;

View File

@@ -287,7 +287,7 @@ public:
/// <param name="drawModes">The draw passes to use for rendering this object.</param>
/// <param name="perInstanceRandom">The random per-instance value (normalized to range 0-1).</param>
/// <param name="sortOrder">Object sorting key.</param>
API_FUNCTION() void Draw(API_PARAM(Ref) const RenderContext& renderContext, MaterialBase* material, API_PARAM(Ref) const Matrix& world, StaticFlags flags = StaticFlags::None, bool receiveDecals = true, DrawPass drawModes = DrawPass::Default, float perInstanceRandom = 0.0f, int16 sortOrder = 0) const;
API_FUNCTION() void Draw(API_PARAM(Ref) const RenderContext& renderContext, MaterialBase* material, API_PARAM(Ref) const Matrix& world, StaticFlags flags = StaticFlags::None, bool receiveDecals = true, DrawPass drawModes = DrawPass::Default, float perInstanceRandom = 0.0f, int8 sortOrder = 0) const;
/// <summary>
/// Draws the mesh.

View File

@@ -240,6 +240,6 @@ public:
/// <summary>
/// The object sorting key.
/// </summary>
int16 SortOrder;
int8 SortOrder;
};
};

View File

@@ -139,7 +139,7 @@ public:
/// <param name="drawModes">The draw passes to use for rendering this object.</param>
/// <param name="perInstanceRandom">The random per-instance value (normalized to range 0-1).</param>
/// <param name="sortOrder">Object sorting key.</param>
API_FUNCTION() void Draw(API_PARAM(Ref) const RenderContext& renderContext, MaterialBase* material, API_PARAM(Ref) const Matrix& world, StaticFlags flags = StaticFlags::None, bool receiveDecals = true, DrawPass drawModes = DrawPass::Default, float perInstanceRandom = 0.0f, int16 sortOrder = 0) const
API_FUNCTION() void Draw(API_PARAM(Ref) const RenderContext& renderContext, MaterialBase* material, API_PARAM(Ref) const Matrix& world, StaticFlags flags = StaticFlags::None, bool receiveDecals = true, DrawPass drawModes = DrawPass::Default, float perInstanceRandom = 0.0f, int8 sortOrder = 0) const
{
for (int32 i = 0; i < Meshes.Count(); i++)
Meshes.Get()[i].Draw(renderContext, material, world, flags, receiveDecals, drawModes, perInstanceRandom, sortOrder);

View File

@@ -150,10 +150,10 @@ public:
DrawPass DrawModes = DrawPass::Default;
/// <summary>
/// The object sort order key used when sorting drawable objects during rendering. Use lower values to draw object before others, higher values are rendered later (on top). Can be use to control transparency drawing.
/// The object sort order key used when sorting drawable objects during rendering. Use lower values to draw object before others, higher values are rendered later (on top). Can be used to control transparency drawing.
/// </summary>
API_FIELD(Attributes="EditorDisplay(\"Skinned Model\"), EditorOrder(110), DefaultValue(0)")
int16 SortOrder = 0;
int8 SortOrder = 0;
/// <summary>
/// The shadows casting mode.

View File

@@ -92,7 +92,7 @@ int32 StaticModel::GetSortOrder() const
void StaticModel::SetSortOrder(int32 value)
{
_sortOrder = (int16)Math::Clamp<int32>(value, MIN_int16, MAX_int16);
_sortOrder = (int8)Math::Clamp<int32>(value, MIN_int8, MAX_int8);
}
bool StaticModel::HasLightmap() const

View File

@@ -22,7 +22,7 @@ private:
char _forcedLod;
bool _vertexColorsDirty;
byte _vertexColorsCount;
int16 _sortOrder;
int8 _sortOrder;
Array<Color32> _vertexColorsData[MODEL_MAX_LODS];
GPUBuffer* _vertexColorsBuffer[MODEL_MAX_LODS];
Model* _residencyChangedModel = nullptr;
@@ -97,13 +97,13 @@ public:
API_PROPERTY() void SetForcedLOD(int32 value);
/// <summary>
/// Gets the model sort order key used when sorting drawable objects during rendering. Use lower values to draw object before others, higher values are rendered later (on top). Can be use to control transparency drawing.
/// Gets the model sort order key used when sorting drawable objects during rendering. Use lower values to draw object before others, higher values are rendered later (on top). Can be used to control transparency drawing.
/// </summary>
API_PROPERTY(Attributes="EditorOrder(60), DefaultValue(0), EditorDisplay(\"Model\")")
int32 GetSortOrder() const;
/// <summary>
/// Sets the model sort order key used when sorting drawable objects during rendering. Use lower values to draw object before others, higher values are rendered later (on top). Can be use to control transparency drawing.
/// Sets the model sort order key used when sorting drawable objects during rendering. Use lower values to draw object before others, higher values are rendered later (on top). Can be used to control transparency drawing.
/// </summary>
API_PROPERTY() void SetSortOrder(int32 value);

View File

@@ -255,10 +255,10 @@ public:
DrawPass DrawModes = DrawPass::Default;
/// <summary>
/// The object sort order key used when sorting drawable objects during rendering. Use lower values to draw object before others, higher values are rendered later (on top). Can be use to control transparency drawing.
/// The object sort order key used when sorting drawable objects during rendering. Use lower values to draw object before others, higher values are rendered later (on top). Can be used to control transparency drawing.
/// </summary>
API_FIELD(Attributes="EditorDisplay(\"Particle Effect\"), EditorOrder(80), DefaultValue(0)")
int16 SortOrder = 0;
int8 SortOrder = 0;
public:
/// <summary>

View File

@@ -161,7 +161,7 @@ void Particles::OnEffectDestroy(ParticleEffect* effect)
typedef Array<int32, FixedAllocation<PARTICLE_EMITTER_MAX_MODULES>> RenderModulesIndices;
void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCall& drawCall, DrawPass drawModes, StaticFlags staticFlags, ParticleEmitterInstance& emitterData, const RenderModulesIndices& renderModulesIndices, int16 sortOrder)
void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCall& drawCall, DrawPass drawModes, StaticFlags staticFlags, ParticleEmitterInstance& emitterData, const RenderModulesIndices& renderModulesIndices, int8 sortOrder)
{
// Skip if CPU buffer is empty
if (buffer->CPU.Count == 0)
@@ -598,7 +598,7 @@ void CleanupGPUParticlesSorting()
GPUParticlesSorting = nullptr;
}
void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCall& drawCall, DrawPass drawModes, StaticFlags staticFlags, ParticleEmitterInstance& emitterData, const RenderModulesIndices& renderModulesIndices, int16 sortOrder)
void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCall& drawCall, DrawPass drawModes, StaticFlags staticFlags, ParticleEmitterInstance& emitterData, const RenderModulesIndices& renderModulesIndices, int8 sortOrder)
{
const auto context = GPUDevice::Instance->GetMainContext();
auto emitter = buffer->Emitter;
@@ -921,7 +921,7 @@ void Particles::DrawParticles(RenderContext& renderContext, ParticleEffect* effe
worldDeterminantSigns[0] = Math::FloatSelect(worlds[0].RotDeterminant(), 1, -1);
worldDeterminantSigns[1] = Math::FloatSelect(worlds[1].RotDeterminant(), 1, -1);
const StaticFlags staticFlags = effect->GetStaticFlags();
const int16 sortOrder = effect->SortOrder;
const int8 sortOrder = effect->SortOrder;
// Draw lights
for (int32 emitterIndex = 0; emitterIndex < effect->Instance.Emitters.Count(); emitterIndex++)

View File

@@ -449,14 +449,16 @@ struct PackedSortKey
struct
{
// Sorting order: By Sort Order -> By Material -> By Geometry -> By Distance
uint32 DistanceKey;
uint8 DrawKey;
uint16 BatchKey;
uint16 SortKey;
uint8 SortKey;
};
};
};
FORCE_INLINE void CalculateSortKey(const RenderContext& renderContext, DrawCall& drawCall, int16 sortOrder)
FORCE_INLINE void CalculateSortKey(const RenderContext& renderContext, DrawCall& drawCall, int8 sortOrder)
{
const Float3 planeNormal = renderContext.View.Direction;
const float planePoint = -Float3::Dot(planeNormal, renderContext.View.Position);
@@ -464,20 +466,33 @@ FORCE_INLINE void CalculateSortKey(const RenderContext& renderContext, DrawCall&
PackedSortKey key;
key.DistanceKey = RenderTools::ComputeDistanceSortKey(distance);
uint32 batchKey = GetHash(drawCall.Material);
batchKey = (batchKey * 397) ^ GetHash(drawCall.Geometry.VertexBuffers[0]);
batchKey = (batchKey * 397) ^ GetHash(drawCall.Geometry.VertexBuffers[1]);
batchKey = (batchKey * 397) ^ GetHash(drawCall.Geometry.VertexBuffers[2]);
batchKey = (batchKey * 397) ^ GetHash(drawCall.Geometry.IndexBuffer);
IMaterial::InstancingHandler handler;
if (drawCall.Material->CanUseInstancing(handler))
handler.GetHash(drawCall, batchKey);
batchKey += (int32)(471 * drawCall.WorldDeterminantSign);
key.SortKey = (uint16)(sortOrder - MIN_int16);
key.BatchKey = (uint16)batchKey;
uint32 drawKey = (uint32)(471 * drawCall.WorldDeterminantSign);
drawKey = (drawKey * 397) ^ GetHash(drawCall.Geometry.VertexBuffers[0]);
drawKey = (drawKey * 397) ^ GetHash(drawCall.Geometry.VertexBuffers[1]);
drawKey = (drawKey * 397) ^ GetHash(drawCall.Geometry.VertexBuffers[2]);
drawKey = (drawKey * 397) ^ GetHash(drawCall.Geometry.IndexBuffer);
key.DrawKey = (uint8)drawKey;
key.SortKey = (uint8)(sortOrder - MIN_int8);
drawCall.SortKey = key.Data;
}
void RenderList::AddDrawCall(const RenderContext& renderContext, DrawPass drawModes, StaticFlags staticFlags, DrawCall& drawCall, bool receivesDecals, int16 sortOrder)
FORCE_INLINE bool CanBatchDrawCalls(const DrawCall& a, const DrawCall& b, DrawPass pass)
{
IMaterial::InstancingHandler handlerA, handlerB;
return a.Material->CanUseInstancing(handlerA) &&
b.Material->CanUseInstancing(handlerB) &&
a.InstanceCount != 0 &&
b.InstanceCount != 0 &&
handlerA.CanBatch == handlerB.CanBatch &&
handlerA.CanBatch(a, b, pass) &&
a.WorldDeterminantSign * b.WorldDeterminantSign > 0;
}
void RenderList::AddDrawCall(const RenderContext& renderContext, DrawPass drawModes, StaticFlags staticFlags, DrawCall& drawCall, bool receivesDecals, int8 sortOrder)
{
#if ENABLE_ASSERTION_LOW_LAYERS
// Ensure that draw modes are non-empty and in conjunction with material draw modes
@@ -515,7 +530,7 @@ void RenderList::AddDrawCall(const RenderContext& renderContext, DrawPass drawMo
}
}
void RenderList::AddDrawCall(const RenderContextBatch& renderContextBatch, DrawPass drawModes, StaticFlags staticFlags, ShadowsCastingMode shadowsMode, const BoundingSphere& bounds, DrawCall& drawCall, bool receivesDecals, int16 sortOrder)
void RenderList::AddDrawCall(const RenderContextBatch& renderContextBatch, DrawPass drawModes, StaticFlags staticFlags, ShadowsCastingMode shadowsMode, const BoundingSphere& bounds, DrawCall& drawCall, bool receivesDecals, int8 sortOrder)
{
#if ENABLE_ASSERTION_LOW_LAYERS
// Ensure that draw modes are non-empty and in conjunction with material draw modes
@@ -571,19 +586,8 @@ void RenderList::AddDrawCall(const RenderContextBatch& renderContextBatch, DrawP
}
}
namespace
{
FORCE_INLINE bool CanBatchWith(const DrawCall& a, const DrawCall& b, DrawPass pass)
{
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 &&
handlerA.CanBatch == handlerB.CanBatch &&
handlerA.CanBatch(a, b, pass) &&
a.WorldDeterminantSign * b.WorldDeterminantSign > 0;
}
}
@@ -639,11 +643,11 @@ void RenderList::SortDrawCalls(const RenderContext& renderContext, bool reverseD
int32 batchSize = 1;
int32 instanceCount = drawCall.InstanceCount;
// Check the following draw calls to merge them (using instancing)
// Check the following draw calls sequence to merge them
for (int32 j = i + 1; j < listSize; j++)
{
const DrawCall& other = drawCallsData[listData[j]];
if (!CanBatchWith(drawCall, other, pass))
if (!CanBatchDrawCalls(drawCall, other, pass))
break;
batchSize++;
instanceCount += other.InstanceCount;
@@ -949,9 +953,7 @@ bool SurfaceDrawCallHandler::CanBatch(const DrawCall& a, const DrawCall& b, Draw
{
// 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)
if (a.Surface.Lightmap == nullptr && b.Surface.Lightmap == nullptr &&
//return a.Surface.Lightmap == b.Surface.Lightmap &&
a.Surface.Skinning == nullptr &&
b.Surface.Skinning == nullptr)
a.Surface.Skinning == nullptr && b.Surface.Skinning == nullptr)
{
if (a.Material != b.Material)
{

View File

@@ -502,7 +502,7 @@ public:
/// <param name="drawCall">The draw call data.</param>
/// <param name="receivesDecals">True if the rendered mesh can receive decals.</param>
/// <param name="sortOrder">Object sorting key.</param>
void AddDrawCall(const RenderContext& renderContext, DrawPass drawModes, StaticFlags staticFlags, DrawCall& drawCall, bool receivesDecals = true, int16 sortOrder = 0);
void AddDrawCall(const RenderContext& renderContext, DrawPass drawModes, StaticFlags staticFlags, DrawCall& drawCall, bool receivesDecals = true, int8 sortOrder = 0);
/// <summary>
/// Adds the draw call to the draw lists and references it in other render contexts. Performs additional per-context frustum culling.
@@ -515,7 +515,7 @@ public:
/// <param name="drawCall">The draw call data.</param>
/// <param name="receivesDecals">True if the rendered mesh can receive decals.</param>
/// <param name="sortOrder">Object sorting key.</param>
void AddDrawCall(const RenderContextBatch& renderContextBatch, DrawPass drawModes, StaticFlags staticFlags, ShadowsCastingMode shadowsMode, const BoundingSphere& bounds, DrawCall& drawCall, bool receivesDecals = true, int16 sortOrder = 0);
void AddDrawCall(const RenderContextBatch& renderContextBatch, DrawPass drawModes, StaticFlags staticFlags, ShadowsCastingMode shadowsMode, const BoundingSphere& bounds, DrawCall& drawCall, bool receivesDecals = true, int8 sortOrder = 0);
/// <summary>
/// Sorts the collected draw calls list.

View File

@@ -85,10 +85,10 @@ public:
DrawPass DrawModes = DrawPass::Default;
/// <summary>
/// Gets the object sort order key used when sorting drawable objects during rendering. Use lower values to draw object before others, higher values are rendered later (on top). Can be use to control transparency drawing.
/// Gets the object sort order key used when sorting drawable objects during rendering. Use lower values to draw object before others, higher values are rendered later (on top). Can be used to control transparency drawing.
/// </summary>
API_FIELD(Attributes="EditorOrder(60), DefaultValue(0), EditorDisplay(\"Sprite\")")
int16 SortOrder = 0;
int8 SortOrder = 0;
private:
void OnMaterialLoaded();

View File

@@ -111,10 +111,10 @@ public:
ShadowsCastingMode ShadowsMode = ShadowsCastingMode::All;
/// <summary>
/// The object sort order key used when sorting drawable objects during rendering. Use lower values to draw object before others, higher values are rendered later (on top). Can be use to control transparency drawing.
/// The object sort order key used when sorting drawable objects during rendering. Use lower values to draw object before others, higher values are rendered later (on top). Can be used to control transparency drawing.
/// </summary>
API_FIELD(Attributes="EditorOrder(85), DefaultValue(0), EditorDisplay(\"Text\")")
int16 SortOrder = 0;
int8 SortOrder = 0;
/// <summary>
/// Gets the layout options. Layout is defined in local space of the object (on XY plane).