From 78f3248ac9aceb5ecfd0e8fb23a846ff7e1a790a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 29 Jun 2024 13:54:02 +0200 Subject: [PATCH] Refactor `SortOrder` to use `int8` instead of `int16` due to performance reasons (more efficent sort keys packing in rendering) --- Source/Engine/Content/Assets/Model.cpp | 2 +- Source/Engine/Content/Assets/Model.h | 2 +- Source/Engine/Graphics/Models/Mesh.cpp | 2 +- Source/Engine/Graphics/Models/Mesh.h | 2 +- Source/Engine/Graphics/Models/MeshBase.h | 2 +- Source/Engine/Graphics/Models/ModelLOD.h | 2 +- Source/Engine/Level/Actors/AnimatedModel.h | 4 +- Source/Engine/Level/Actors/StaticModel.cpp | 2 +- Source/Engine/Level/Actors/StaticModel.h | 6 +-- Source/Engine/Particles/ParticleEffect.h | 4 +- Source/Engine/Particles/Particles.cpp | 6 +-- Source/Engine/Renderer/RenderList.cpp | 54 +++++++++++----------- Source/Engine/Renderer/RenderList.h | 4 +- Source/Engine/UI/SpriteRender.h | 4 +- Source/Engine/UI/TextRender.h | 4 +- 15 files changed, 51 insertions(+), 49 deletions(-) diff --git a/Source/Engine/Content/Assets/Model.cpp b/Source/Engine/Content/Assets/Model.cpp index 33d244d0c..22de66263 100644 --- a/Source/Engine/Content/Assets/Model.cpp +++ b/Source/Engine/Content/Assets/Model.cpp @@ -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; diff --git a/Source/Engine/Content/Assets/Model.h b/Source/Engine/Content/Assets/Model.h index 5c5310bb9..dd52ba34a 100644 --- a/Source/Engine/Content/Assets/Model.h +++ b/Source/Engine/Content/Assets/Model.h @@ -182,7 +182,7 @@ public: /// The object static flags. /// True if rendered geometry can receive decals, otherwise false. /// Object sorting key. - 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; /// /// Draws the model. diff --git a/Source/Engine/Graphics/Models/Mesh.cpp b/Source/Engine/Graphics/Models/Mesh.cpp index fda0550ba..f168ccd79 100644 --- a/Source/Engine/Graphics/Models/Mesh.cpp +++ b/Source/Engine/Graphics/Models/Mesh.cpp @@ -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; diff --git a/Source/Engine/Graphics/Models/Mesh.h b/Source/Engine/Graphics/Models/Mesh.h index 9b229fc5b..813836b85 100644 --- a/Source/Engine/Graphics/Models/Mesh.h +++ b/Source/Engine/Graphics/Models/Mesh.h @@ -287,7 +287,7 @@ public: /// The draw passes to use for rendering this object. /// The random per-instance value (normalized to range 0-1). /// Object sorting key. - 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; /// /// Draws the mesh. diff --git a/Source/Engine/Graphics/Models/MeshBase.h b/Source/Engine/Graphics/Models/MeshBase.h index 9963ec8ba..467a88ea3 100644 --- a/Source/Engine/Graphics/Models/MeshBase.h +++ b/Source/Engine/Graphics/Models/MeshBase.h @@ -240,6 +240,6 @@ public: /// /// The object sorting key. /// - int16 SortOrder; + int8 SortOrder; }; }; diff --git a/Source/Engine/Graphics/Models/ModelLOD.h b/Source/Engine/Graphics/Models/ModelLOD.h index c367a1830..a6937739e 100644 --- a/Source/Engine/Graphics/Models/ModelLOD.h +++ b/Source/Engine/Graphics/Models/ModelLOD.h @@ -139,7 +139,7 @@ public: /// The draw passes to use for rendering this object. /// The random per-instance value (normalized to range 0-1). /// Object sorting key. - 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); diff --git a/Source/Engine/Level/Actors/AnimatedModel.h b/Source/Engine/Level/Actors/AnimatedModel.h index bb4b3a8f0..753e72080 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.h +++ b/Source/Engine/Level/Actors/AnimatedModel.h @@ -150,10 +150,10 @@ public: DrawPass DrawModes = DrawPass::Default; /// - /// 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. /// API_FIELD(Attributes="EditorDisplay(\"Skinned Model\"), EditorOrder(110), DefaultValue(0)") - int16 SortOrder = 0; + int8 SortOrder = 0; /// /// The shadows casting mode. diff --git a/Source/Engine/Level/Actors/StaticModel.cpp b/Source/Engine/Level/Actors/StaticModel.cpp index dd77692b3..c735b2b5f 100644 --- a/Source/Engine/Level/Actors/StaticModel.cpp +++ b/Source/Engine/Level/Actors/StaticModel.cpp @@ -92,7 +92,7 @@ int32 StaticModel::GetSortOrder() const void StaticModel::SetSortOrder(int32 value) { - _sortOrder = (int16)Math::Clamp(value, MIN_int16, MAX_int16); + _sortOrder = (int8)Math::Clamp(value, MIN_int8, MAX_int8); } bool StaticModel::HasLightmap() const diff --git a/Source/Engine/Level/Actors/StaticModel.h b/Source/Engine/Level/Actors/StaticModel.h index 5a986d0fb..a8171d6ad 100644 --- a/Source/Engine/Level/Actors/StaticModel.h +++ b/Source/Engine/Level/Actors/StaticModel.h @@ -22,7 +22,7 @@ private: char _forcedLod; bool _vertexColorsDirty; byte _vertexColorsCount; - int16 _sortOrder; + int8 _sortOrder; Array _vertexColorsData[MODEL_MAX_LODS]; GPUBuffer* _vertexColorsBuffer[MODEL_MAX_LODS]; Model* _residencyChangedModel = nullptr; @@ -97,13 +97,13 @@ public: API_PROPERTY() void SetForcedLOD(int32 value); /// - /// 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. /// API_PROPERTY(Attributes="EditorOrder(60), DefaultValue(0), EditorDisplay(\"Model\")") int32 GetSortOrder() const; /// - /// 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. /// API_PROPERTY() void SetSortOrder(int32 value); diff --git a/Source/Engine/Particles/ParticleEffect.h b/Source/Engine/Particles/ParticleEffect.h index 7cf3abfad..1e2136ff9 100644 --- a/Source/Engine/Particles/ParticleEffect.h +++ b/Source/Engine/Particles/ParticleEffect.h @@ -255,10 +255,10 @@ public: DrawPass DrawModes = DrawPass::Default; /// - /// 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. /// API_FIELD(Attributes="EditorDisplay(\"Particle Effect\"), EditorOrder(80), DefaultValue(0)") - int16 SortOrder = 0; + int8 SortOrder = 0; public: /// diff --git a/Source/Engine/Particles/Particles.cpp b/Source/Engine/Particles/Particles.cpp index c7d9e01d8..4b869bf42 100644 --- a/Source/Engine/Particles/Particles.cpp +++ b/Source/Engine/Particles/Particles.cpp @@ -161,7 +161,7 @@ void Particles::OnEffectDestroy(ParticleEffect* effect) typedef Array> 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++) diff --git a/Source/Engine/Renderer/RenderList.cpp b/Source/Engine/Renderer/RenderList.cpp index 154fed7c4..b85bbed1b 100644 --- a/Source/Engine/Renderer/RenderList.cpp +++ b/Source/Engine/Renderer/RenderList.cpp @@ -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) { diff --git a/Source/Engine/Renderer/RenderList.h b/Source/Engine/Renderer/RenderList.h index 8f8e86973..5b8c66647 100644 --- a/Source/Engine/Renderer/RenderList.h +++ b/Source/Engine/Renderer/RenderList.h @@ -502,7 +502,7 @@ public: /// The draw call data. /// True if the rendered mesh can receive decals. /// Object sorting key. - 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); /// /// 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: /// The draw call data. /// True if the rendered mesh can receive decals. /// Object sorting key. - 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); /// /// Sorts the collected draw calls list. diff --git a/Source/Engine/UI/SpriteRender.h b/Source/Engine/UI/SpriteRender.h index d9eb769c2..bd7294c31 100644 --- a/Source/Engine/UI/SpriteRender.h +++ b/Source/Engine/UI/SpriteRender.h @@ -85,10 +85,10 @@ public: DrawPass DrawModes = DrawPass::Default; /// - /// 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. /// API_FIELD(Attributes="EditorOrder(60), DefaultValue(0), EditorDisplay(\"Sprite\")") - int16 SortOrder = 0; + int8 SortOrder = 0; private: void OnMaterialLoaded(); diff --git a/Source/Engine/UI/TextRender.h b/Source/Engine/UI/TextRender.h index cc89fbee1..57e48ac9b 100644 --- a/Source/Engine/UI/TextRender.h +++ b/Source/Engine/UI/TextRender.h @@ -111,10 +111,10 @@ public: ShadowsCastingMode ShadowsMode = ShadowsCastingMode::All; /// - /// 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. /// API_FIELD(Attributes="EditorOrder(85), DefaultValue(0), EditorDisplay(\"Text\")") - int16 SortOrder = 0; + int8 SortOrder = 0; /// /// Gets the layout options. Layout is defined in local space of the object (on XY plane).