diff --git a/engine/include/XCEngine/Rendering/RenderSceneUtility.h b/engine/include/XCEngine/Rendering/RenderSceneUtility.h index ad7020dd..c8bef29f 100644 --- a/engine/include/XCEngine/Rendering/RenderSceneUtility.h +++ b/engine/include/XCEngine/Rendering/RenderSceneUtility.h @@ -27,6 +27,10 @@ void AppendRenderItemsForGameObject( const Math::Vector3& cameraPosition, std::vector& outVisibleItems); +bool CompareVisibleRenderItemsStable( + const VisibleRenderItem& lhs, + const VisibleRenderItem& rhs); + std::vector CollectRenderItemsForEntityIds( const Components::Scene& scene, const std::vector& entityIds, diff --git a/engine/src/Rendering/RenderSceneExtractor.cpp b/engine/src/Rendering/RenderSceneExtractor.cpp index 3bda52d5..5eeac635 100644 --- a/engine/src/Rendering/RenderSceneExtractor.cpp +++ b/engine/src/Rendering/RenderSceneExtractor.cpp @@ -113,22 +113,7 @@ uint32_t GetAdditionalLightTypeRank(const Components::LightComponent& light) { } bool CompareVisibleItems(const VisibleRenderItem& lhs, const VisibleRenderItem& rhs) { - if (lhs.renderQueue != rhs.renderQueue) { - return lhs.renderQueue < rhs.renderQueue; - } - - const bool isTransparentQueue = IsTransparentRenderQueue(lhs.renderQueue); - if (lhs.cameraDistanceSq != rhs.cameraDistanceSq) { - return isTransparentQueue - ? lhs.cameraDistanceSq > rhs.cameraDistanceSq - : lhs.cameraDistanceSq < rhs.cameraDistanceSq; - } - - if (lhs.gameObject != rhs.gameObject) { - return lhs.gameObject < rhs.gameObject; - } - - return lhs.sectionIndex < rhs.sectionIndex; + return CompareVisibleRenderItemsStable(lhs, rhs); } } // namespace diff --git a/engine/src/Rendering/RenderSceneUtility.cpp b/engine/src/Rendering/RenderSceneUtility.cpp index 598a86e5..d68dea04 100644 --- a/engine/src/Rendering/RenderSceneUtility.cpp +++ b/engine/src/Rendering/RenderSceneUtility.cpp @@ -116,6 +116,41 @@ void AppendRenderItemsForGameObject( outVisibleItems.push_back(visibleItem); } +bool CompareVisibleRenderItemsStable( + const VisibleRenderItem& lhs, + const VisibleRenderItem& rhs) { + if (lhs.renderQueue != rhs.renderQueue) { + return lhs.renderQueue < rhs.renderQueue; + } + + const bool isTransparentQueue = IsTransparentRenderQueue(lhs.renderQueue); + if (lhs.cameraDistanceSq != rhs.cameraDistanceSq) { + return isTransparentQueue + ? lhs.cameraDistanceSq > rhs.cameraDistanceSq + : lhs.cameraDistanceSq < rhs.cameraDistanceSq; + } + + const Core::uint64 lhsObjectId = lhs.gameObject != nullptr ? lhs.gameObject->GetID() : 0u; + const Core::uint64 rhsObjectId = rhs.gameObject != nullptr ? rhs.gameObject->GetID() : 0u; + if (lhsObjectId != rhsObjectId) { + return lhsObjectId < rhsObjectId; + } + + if (lhs.hasSection != rhs.hasSection) { + return lhs.hasSection && !rhs.hasSection; + } + + if (lhs.sectionIndex != rhs.sectionIndex) { + return lhs.sectionIndex < rhs.sectionIndex; + } + + if (lhs.materialIndex != rhs.materialIndex) { + return lhs.materialIndex < rhs.materialIndex; + } + + return false; +} + std::vector CollectRenderItemsForEntityIds( const Components::Scene& scene, const std::vector& entityIds, diff --git a/tests/Rendering/unit/test_render_scene_utility.cpp b/tests/Rendering/unit/test_render_scene_utility.cpp index 35d40dce..ad8c9594 100644 --- a/tests/Rendering/unit/test_render_scene_utility.cpp +++ b/tests/Rendering/unit/test_render_scene_utility.cpp @@ -11,6 +11,8 @@ #include #include +#include +#include #include namespace { @@ -24,7 +26,9 @@ using XCEngine::Components::Scene; using XCEngine::Math::Matrix4x4; using XCEngine::Math::Vector3; using XCEngine::Rendering::BuildRenderCameraData; +using XCEngine::Rendering::CompareVisibleRenderItemsStable; using XCEngine::Rendering::CollectRenderItemsForEntityIds; +using XCEngine::Rendering::VisibleRenderItem; using XCEngine::Resources::Mesh; using XCEngine::Resources::MeshSection; using XCEngine::Resources::StaticMeshVertex; @@ -211,4 +215,33 @@ TEST_F(RenderSceneUtilityTest, CollectRenderItemsForEntityIdsExpandsMeshSections EXPECT_EQ(renderables[1].materialIndex, 1u); } +TEST(RenderSceneUtilityStandaloneTest, CompareVisibleRenderItemsUsesStableObjectIdOrdering) { + using Storage = std::aligned_storage_t; + + Storage storage[2]; + GameObject* lowerIdHigherAddress = new (&storage[1]) GameObject("LowerId"); + GameObject* higherIdLowerAddress = new (&storage[0]) GameObject("HigherId"); + + ASSERT_LT(lowerIdHigherAddress->GetID(), higherIdLowerAddress->GetID()); + ASSERT_GT( + reinterpret_cast(lowerIdHigherAddress), + reinterpret_cast(higherIdLowerAddress)); + + VisibleRenderItem lhs = {}; + lhs.gameObject = lowerIdHigherAddress; + lhs.renderQueue = 2000; + lhs.cameraDistanceSq = 9.0f; + + VisibleRenderItem rhs = {}; + rhs.gameObject = higherIdLowerAddress; + rhs.renderQueue = 2000; + rhs.cameraDistanceSq = 9.0f; + + EXPECT_TRUE(CompareVisibleRenderItemsStable(lhs, rhs)); + EXPECT_FALSE(CompareVisibleRenderItemsStable(rhs, lhs)); + + higherIdLowerAddress->~GameObject(); + lowerIdHigherAddress->~GameObject(); +} + } // namespace