diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index 018b48ec..49ea811c 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -89,6 +89,7 @@ add_executable(${PROJECT_NAME} WIN32 src/Viewport/SceneViewportTransformGizmoCoordinator.cpp src/Viewport/SceneViewportOrientationGizmo.cpp src/Viewport/SceneViewportOverlayBuilder.cpp + src/Viewport/SceneViewportOverlayFrameCache.cpp src/Viewport/SceneViewportOverlaySpriteResources.cpp src/Viewport/SceneViewportOverlayProviders.cpp src/Viewport/Passes/SceneViewportEditorOverlayPass.cpp diff --git a/editor/src/Viewport/SceneViewportOverlayFrameCache.cpp b/editor/src/Viewport/SceneViewportOverlayFrameCache.cpp new file mode 100644 index 00000000..8b29862d --- /dev/null +++ b/editor/src/Viewport/SceneViewportOverlayFrameCache.cpp @@ -0,0 +1,200 @@ +#include "Viewport/SceneViewportOverlayFrameCache.h" + +#include +#include +#include +#include +#include + +#include +#include + +namespace XCEngine { +namespace Editor { + +namespace { + +constexpr uint64_t kSceneViewportOverlaySignatureOffsetBasis = 14695981039346656037ull; +constexpr uint64_t kSceneViewportOverlaySignaturePrime = 1099511628211ull; + +void HashSceneViewportOverlayBytes(uint64_t& hash, const void* data, size_t size) { + const auto* bytes = static_cast(data); + for (size_t index = 0; index < size; ++index) { + hash ^= static_cast(bytes[index]); + hash *= kSceneViewportOverlaySignaturePrime; + } +} + +template +void HashSceneViewportOverlayValue(uint64_t& hash, const TValue& value) { + HashSceneViewportOverlayBytes(hash, &value, sizeof(TValue)); +} + +void HashSceneViewportOverlayFloat(uint64_t& hash, float value) { + uint32_t bits = 0u; + std::memcpy(&bits, &value, sizeof(bits)); + HashSceneViewportOverlayValue(hash, bits); +} + +void HashSceneViewportOverlayVector3(uint64_t& hash, const Math::Vector3& value) { + HashSceneViewportOverlayFloat(hash, value.x); + HashSceneViewportOverlayFloat(hash, value.y); + HashSceneViewportOverlayFloat(hash, value.z); +} + +void HashSceneViewportOverlayQuaternion(uint64_t& hash, const Math::Quaternion& value) { + HashSceneViewportOverlayFloat(hash, value.x); + HashSceneViewportOverlayFloat(hash, value.y); + HashSceneViewportOverlayFloat(hash, value.z); + HashSceneViewportOverlayFloat(hash, value.w); +} + +void HashSceneViewportOverlayRect(uint64_t& hash, const Math::Rect& value) { + HashSceneViewportOverlayFloat(hash, value.x); + HashSceneViewportOverlayFloat(hash, value.y); + HashSceneViewportOverlayFloat(hash, value.width); + HashSceneViewportOverlayFloat(hash, value.height); +} + +void HashSceneViewportOverlayTransform(uint64_t& hash, const Components::TransformComponent& transform) { + HashSceneViewportOverlayVector3(hash, transform.GetPosition()); + HashSceneViewportOverlayQuaternion(hash, transform.GetRotation()); + HashSceneViewportOverlayVector3(hash, transform.GetScale()); +} + +bool AreEqualSceneViewportVector3(const Math::Vector3& lhs, const Math::Vector3& rhs) { + constexpr float kEpsilon = 1e-4f; + return std::abs(lhs.x - rhs.x) <= kEpsilon && + std::abs(lhs.y - rhs.y) <= kEpsilon && + std::abs(lhs.z - rhs.z) <= kEpsilon; +} + +} // namespace + +void ResetSceneViewportOverlayFrameCacheState( + SceneViewportOverlayFrameCacheState& cacheState) { + cacheState = {}; +} + +void ResolveSceneViewportOverlayFrameViewportSize( + uint32_t requestedWidth, + uint32_t requestedHeight, + uint32_t renderWidth, + uint32_t renderHeight, + uint32_t& outWidth, + uint32_t& outHeight) { + outWidth = requestedWidth > 0u ? requestedWidth : renderWidth; + outHeight = requestedHeight > 0u ? requestedHeight : renderHeight; +} + +bool AreEqualSceneViewportOverlayData( + const SceneViewportOverlayData& lhs, + const SceneViewportOverlayData& rhs) { + constexpr float kEpsilon = 1e-4f; + return lhs.valid == rhs.valid && + AreEqualSceneViewportVector3(lhs.cameraPosition, rhs.cameraPosition) && + AreEqualSceneViewportVector3(lhs.cameraForward, rhs.cameraForward) && + AreEqualSceneViewportVector3(lhs.cameraRight, rhs.cameraRight) && + AreEqualSceneViewportVector3(lhs.cameraUp, rhs.cameraUp) && + std::abs(lhs.verticalFovDegrees - rhs.verticalFovDegrees) <= kEpsilon && + std::abs(lhs.nearClipPlane - rhs.nearClipPlane) <= kEpsilon && + std::abs(lhs.farClipPlane - rhs.farClipPlane) <= kEpsilon && + std::abs(lhs.orbitDistance - rhs.orbitDistance) <= kEpsilon; +} + +uint64_t BuildSceneViewportOverlayContentSignature( + const Components::Scene* scene, + const std::vector& selectedObjectIds) { + uint64_t hash = kSceneViewportOverlaySignatureOffsetBasis; + + HashSceneViewportOverlayValue(hash, static_cast(selectedObjectIds.size())); + for (uint64_t entityId : selectedObjectIds) { + HashSceneViewportOverlayValue(hash, entityId); + } + + if (scene == nullptr) { + return hash; + } + + for (Components::CameraComponent* camera : scene->FindObjectsOfType()) { + Components::GameObject* gameObject = camera != nullptr ? camera->GetGameObject() : nullptr; + HashSceneViewportOverlayValue(hash, static_cast(1u)); + HashSceneViewportOverlayValue(hash, gameObject != nullptr ? gameObject->GetID() : 0ull); + HashSceneViewportOverlayValue(hash, camera != nullptr && camera->IsEnabled()); + HashSceneViewportOverlayValue(hash, gameObject != nullptr && gameObject->IsActiveInHierarchy()); + if (camera == nullptr || + gameObject == nullptr || + !camera->IsEnabled() || + !gameObject->IsActiveInHierarchy() || + gameObject->GetTransform() == nullptr) { + continue; + } + + HashSceneViewportOverlayTransform(hash, *gameObject->GetTransform()); + HashSceneViewportOverlayValue(hash, static_cast(camera->GetProjectionType())); + HashSceneViewportOverlayFloat(hash, camera->GetFieldOfView()); + HashSceneViewportOverlayFloat(hash, camera->GetOrthographicSize()); + HashSceneViewportOverlayFloat(hash, camera->GetNearClipPlane()); + HashSceneViewportOverlayFloat(hash, camera->GetFarClipPlane()); + HashSceneViewportOverlayRect(hash, camera->GetViewportRect()); + } + + for (Components::LightComponent* light : scene->FindObjectsOfType()) { + Components::GameObject* gameObject = light != nullptr ? light->GetGameObject() : nullptr; + HashSceneViewportOverlayValue(hash, static_cast(2u)); + HashSceneViewportOverlayValue(hash, gameObject != nullptr ? gameObject->GetID() : 0ull); + HashSceneViewportOverlayValue(hash, light != nullptr && light->IsEnabled()); + HashSceneViewportOverlayValue(hash, gameObject != nullptr && gameObject->IsActiveInHierarchy()); + if (light == nullptr || + gameObject == nullptr || + !light->IsEnabled() || + !gameObject->IsActiveInHierarchy() || + gameObject->GetTransform() == nullptr) { + continue; + } + + HashSceneViewportOverlayTransform(hash, *gameObject->GetTransform()); + HashSceneViewportOverlayValue(hash, static_cast(light->GetLightType())); + } + + return hash; +} + +bool ShouldRebuildSceneViewportOverlayFrameCache( + const SceneViewportOverlayFrameCacheState& cacheState, + const Components::Scene* scene, + const SceneViewportOverlayData& overlay, + uint32_t viewportWidth, + uint32_t viewportHeight, + const std::vector& selectedObjectIds, + uint64_t contentSignature, + bool transformGizmoOverlayDirty) { + return transformGizmoOverlayDirty || + !cacheState.cached || + cacheState.scene != scene || + cacheState.viewportWidth != viewportWidth || + cacheState.viewportHeight != viewportHeight || + cacheState.selectedObjectIds != selectedObjectIds || + cacheState.contentSignature != contentSignature || + !AreEqualSceneViewportOverlayData(cacheState.frameData.overlay, overlay); +} + +void UpdateSceneViewportOverlayFrameCacheState( + SceneViewportOverlayFrameCacheState& cacheState, + const Components::Scene* scene, + uint32_t viewportWidth, + uint32_t viewportHeight, + const std::vector& selectedObjectIds, + uint64_t contentSignature, + const SceneViewportOverlayFrameData& frameData) { + cacheState.frameData = frameData; + cacheState.scene = scene; + cacheState.selectedObjectIds = selectedObjectIds; + cacheState.viewportWidth = viewportWidth; + cacheState.viewportHeight = viewportHeight; + cacheState.contentSignature = contentSignature; + cacheState.cached = true; +} + +} // namespace Editor +} // namespace XCEngine diff --git a/editor/src/Viewport/SceneViewportOverlayFrameCache.h b/editor/src/Viewport/SceneViewportOverlayFrameCache.h new file mode 100644 index 00000000..4aef56bb --- /dev/null +++ b/editor/src/Viewport/SceneViewportOverlayFrameCache.h @@ -0,0 +1,64 @@ +#pragma once + +#include "SceneViewportEditorOverlayData.h" + +#include +#include + +namespace XCEngine { +namespace Components { +class Scene; +} // namespace Components + +namespace Editor { + +struct SceneViewportOverlayFrameCacheState { + SceneViewportOverlayFrameData frameData = {}; + const Components::Scene* scene = nullptr; + std::vector selectedObjectIds = {}; + uint32_t viewportWidth = 0u; + uint32_t viewportHeight = 0u; + uint64_t contentSignature = 0u; + bool cached = false; +}; + +void ResetSceneViewportOverlayFrameCacheState( + SceneViewportOverlayFrameCacheState& cacheState); + +void ResolveSceneViewportOverlayFrameViewportSize( + uint32_t requestedWidth, + uint32_t requestedHeight, + uint32_t renderWidth, + uint32_t renderHeight, + uint32_t& outWidth, + uint32_t& outHeight); + +bool AreEqualSceneViewportOverlayData( + const SceneViewportOverlayData& lhs, + const SceneViewportOverlayData& rhs); + +uint64_t BuildSceneViewportOverlayContentSignature( + const Components::Scene* scene, + const std::vector& selectedObjectIds); + +bool ShouldRebuildSceneViewportOverlayFrameCache( + const SceneViewportOverlayFrameCacheState& cacheState, + const Components::Scene* scene, + const SceneViewportOverlayData& overlay, + uint32_t viewportWidth, + uint32_t viewportHeight, + const std::vector& selectedObjectIds, + uint64_t contentSignature, + bool transformGizmoOverlayDirty); + +void UpdateSceneViewportOverlayFrameCacheState( + SceneViewportOverlayFrameCacheState& cacheState, + const Components::Scene* scene, + uint32_t viewportWidth, + uint32_t viewportHeight, + const std::vector& selectedObjectIds, + uint64_t contentSignature, + const SceneViewportOverlayFrameData& frameData); + +} // namespace Editor +} // namespace XCEngine diff --git a/editor/src/Viewport/ViewportHostService.h b/editor/src/Viewport/ViewportHostService.h index 3e0a7237..d4971f13 100644 --- a/editor/src/Viewport/ViewportHostService.h +++ b/editor/src/Viewport/ViewportHostService.h @@ -9,6 +9,7 @@ #include "Passes/SceneViewportSelectionOutlinePass.h" #include "SceneViewportCameraController.h" #include "SceneViewportEditorOverlayData.h" +#include "SceneViewportOverlayFrameCache.h" #include "SceneViewportOverlayHandleBuilder.h" #include "SceneViewportOverlayBuilder.h" #include "SceneViewportRenderPlan.h" @@ -19,22 +20,18 @@ #include #include -#include #include #include #include #include #include -#include #include #include #include #include #include -#include #include -#include #include #include #include @@ -45,133 +42,6 @@ namespace Editor { namespace { constexpr bool kDebugSceneSelectionMask = false; -constexpr uint64_t kSceneViewportOverlaySignatureOffsetBasis = 14695981039346656037ull; -constexpr uint64_t kSceneViewportOverlaySignaturePrime = 1099511628211ull; - -void HashSceneViewportOverlayBytes(uint64_t& hash, const void* data, size_t size) { - const auto* bytes = static_cast(data); - for (size_t index = 0; index < size; ++index) { - hash ^= static_cast(bytes[index]); - hash *= kSceneViewportOverlaySignaturePrime; - } -} - -template -void HashSceneViewportOverlayValue(uint64_t& hash, const TValue& value) { - HashSceneViewportOverlayBytes(hash, &value, sizeof(TValue)); -} - -void HashSceneViewportOverlayFloat(uint64_t& hash, float value) { - uint32_t bits = 0u; - std::memcpy(&bits, &value, sizeof(bits)); - HashSceneViewportOverlayValue(hash, bits); -} - -void HashSceneViewportOverlayVector3(uint64_t& hash, const Math::Vector3& value) { - HashSceneViewportOverlayFloat(hash, value.x); - HashSceneViewportOverlayFloat(hash, value.y); - HashSceneViewportOverlayFloat(hash, value.z); -} - -void HashSceneViewportOverlayQuaternion(uint64_t& hash, const Math::Quaternion& value) { - HashSceneViewportOverlayFloat(hash, value.x); - HashSceneViewportOverlayFloat(hash, value.y); - HashSceneViewportOverlayFloat(hash, value.z); - HashSceneViewportOverlayFloat(hash, value.w); -} - -void HashSceneViewportOverlayRect(uint64_t& hash, const Math::Rect& value) { - HashSceneViewportOverlayFloat(hash, value.x); - HashSceneViewportOverlayFloat(hash, value.y); - HashSceneViewportOverlayFloat(hash, value.width); - HashSceneViewportOverlayFloat(hash, value.height); -} - -void HashSceneViewportOverlayTransform(uint64_t& hash, const Components::TransformComponent& transform) { - HashSceneViewportOverlayVector3(hash, transform.GetPosition()); - HashSceneViewportOverlayQuaternion(hash, transform.GetRotation()); - HashSceneViewportOverlayVector3(hash, transform.GetScale()); -} - -uint64_t BuildSceneViewEditorOverlayContentSignature( - const Components::Scene* scene, - const std::vector& selectedObjectIds) { - uint64_t hash = kSceneViewportOverlaySignatureOffsetBasis; - - HashSceneViewportOverlayValue(hash, static_cast(selectedObjectIds.size())); - for (uint64_t entityId : selectedObjectIds) { - HashSceneViewportOverlayValue(hash, entityId); - } - - if (scene == nullptr) { - return hash; - } - - for (Components::CameraComponent* camera : scene->FindObjectsOfType()) { - Components::GameObject* gameObject = camera != nullptr ? camera->GetGameObject() : nullptr; - HashSceneViewportOverlayValue(hash, static_cast(1u)); - HashSceneViewportOverlayValue(hash, gameObject != nullptr ? gameObject->GetID() : 0ull); - HashSceneViewportOverlayValue(hash, camera != nullptr && camera->IsEnabled()); - HashSceneViewportOverlayValue(hash, gameObject != nullptr && gameObject->IsActiveInHierarchy()); - if (camera == nullptr || - gameObject == nullptr || - !camera->IsEnabled() || - !gameObject->IsActiveInHierarchy() || - gameObject->GetTransform() == nullptr) { - continue; - } - - HashSceneViewportOverlayTransform(hash, *gameObject->GetTransform()); - HashSceneViewportOverlayValue(hash, static_cast(camera->GetProjectionType())); - HashSceneViewportOverlayFloat(hash, camera->GetFieldOfView()); - HashSceneViewportOverlayFloat(hash, camera->GetOrthographicSize()); - HashSceneViewportOverlayFloat(hash, camera->GetNearClipPlane()); - HashSceneViewportOverlayFloat(hash, camera->GetFarClipPlane()); - HashSceneViewportOverlayRect(hash, camera->GetViewportRect()); - } - - for (Components::LightComponent* light : scene->FindObjectsOfType()) { - Components::GameObject* gameObject = light != nullptr ? light->GetGameObject() : nullptr; - HashSceneViewportOverlayValue(hash, static_cast(2u)); - HashSceneViewportOverlayValue(hash, gameObject != nullptr ? gameObject->GetID() : 0ull); - HashSceneViewportOverlayValue(hash, light != nullptr && light->IsEnabled()); - HashSceneViewportOverlayValue(hash, gameObject != nullptr && gameObject->IsActiveInHierarchy()); - if (light == nullptr || - gameObject == nullptr || - !light->IsEnabled() || - !gameObject->IsActiveInHierarchy() || - gameObject->GetTransform() == nullptr) { - continue; - } - - HashSceneViewportOverlayTransform(hash, *gameObject->GetTransform()); - HashSceneViewportOverlayValue(hash, static_cast(light->GetLightType())); - } - - return hash; -} - -bool AreEqualSceneViewportVector3(const Math::Vector3& lhs, const Math::Vector3& rhs) { - constexpr float kEpsilon = 1e-4f; - return std::abs(lhs.x - rhs.x) <= kEpsilon && - std::abs(lhs.y - rhs.y) <= kEpsilon && - std::abs(lhs.z - rhs.z) <= kEpsilon; -} - -bool AreEqualSceneViewportOverlayData( - const SceneViewportOverlayData& lhs, - const SceneViewportOverlayData& rhs) { - constexpr float kEpsilon = 1e-4f; - return lhs.valid == rhs.valid && - AreEqualSceneViewportVector3(lhs.cameraPosition, rhs.cameraPosition) && - AreEqualSceneViewportVector3(lhs.cameraForward, rhs.cameraForward) && - AreEqualSceneViewportVector3(lhs.cameraRight, rhs.cameraRight) && - AreEqualSceneViewportVector3(lhs.cameraUp, rhs.cameraUp) && - std::abs(lhs.verticalFovDegrees - rhs.verticalFovDegrees) <= kEpsilon && - std::abs(lhs.nearClipPlane - rhs.nearClipPlane) <= kEpsilon && - std::abs(lhs.farClipPlane - rhs.farClipPlane) <= kEpsilon && - std::abs(lhs.orbitDistance - rhs.orbitDistance) <= kEpsilon; -} Math::Vector3 GetSceneViewportOrientationAxisVector(SceneViewportOrientationAxis axis) { switch (axis) { @@ -212,7 +82,7 @@ public: m_sceneViewportGridRenderer.Shutdown(); m_sceneViewportEditorOverlayRenderer.Shutdown(); m_sceneViewCamera = {}; - ResetSceneViewEditorOverlayFrameData(); + ResetSceneViewportOverlayFrameCacheState(m_sceneViewEditorOverlayFrameCache); m_sceneViewTransformGizmoOverlayState = {}; m_sceneViewTransformGizmoOverlayDirty = false; m_sceneViewLastRenderContext = {}; @@ -359,7 +229,7 @@ public: const SceneViewportOverlayFrameData& GetSceneViewEditorOverlayFrameData(IEditorContext& context) override { EnsureSceneViewEditorOverlayFrameData(context); - return m_sceneViewEditorOverlayFrameData; + return m_sceneViewEditorOverlayFrameCache.frameData; } void SetSceneViewTransformGizmoOverlayState( @@ -520,22 +390,17 @@ private: return BuildViewportColorSurface(entry.renderTargets); } - void ResetSceneViewEditorOverlayFrameData() { - m_sceneViewEditorOverlayFrameData = {}; - m_sceneViewEditorOverlayScene = nullptr; - m_sceneViewEditorOverlaySelectedObjectIds.clear(); - m_sceneViewEditorOverlayViewportWidth = 0u; - m_sceneViewEditorOverlayViewportHeight = 0u; - m_sceneViewEditorOverlayContentSignature = 0u; - m_sceneViewEditorOverlayCached = false; - } - void ResolveSceneViewEditorOverlayViewportSize( const ViewportEntry& entry, uint32_t& outWidth, uint32_t& outHeight) const { - outWidth = entry.requestedWidth > 0u ? entry.requestedWidth : entry.renderTargets.width; - outHeight = entry.requestedHeight > 0u ? entry.requestedHeight : entry.renderTargets.height; + ResolveSceneViewportOverlayFrameViewportSize( + entry.requestedWidth, + entry.requestedHeight, + entry.renderTargets.width, + entry.renderTargets.height, + outWidth, + outHeight); } bool ShouldRebuildSceneViewEditorOverlayFrameData( @@ -545,19 +410,20 @@ private: uint32_t viewportHeight, const std::vector& selectedObjectIds, uint64_t contentSignature) const { - return m_sceneViewTransformGizmoOverlayDirty || - !m_sceneViewEditorOverlayCached || - m_sceneViewEditorOverlayScene != scene || - m_sceneViewEditorOverlayViewportWidth != viewportWidth || - m_sceneViewEditorOverlayViewportHeight != viewportHeight || - m_sceneViewEditorOverlaySelectedObjectIds != selectedObjectIds || - m_sceneViewEditorOverlayContentSignature != contentSignature || - !AreEqualSceneViewportOverlayData(m_sceneViewEditorOverlayFrameData.overlay, overlay); + return ShouldRebuildSceneViewportOverlayFrameCache( + m_sceneViewEditorOverlayFrameCache, + scene, + overlay, + viewportWidth, + viewportHeight, + selectedObjectIds, + contentSignature, + m_sceneViewTransformGizmoOverlayDirty); } void EnsureSceneViewEditorOverlayFrameData(IEditorContext& context) { if (!EnsureSceneViewCamera()) { - ResetSceneViewEditorOverlayFrameData(); + ResetSceneViewportOverlayFrameCacheState(m_sceneViewEditorOverlayFrameCache); return; } @@ -570,7 +436,7 @@ private: const SceneViewportOverlayData overlay = GetSceneViewOverlayData(); const std::vector selectedObjectIds = context.GetSelectionManager().GetSelectedEntities(); const uint64_t contentSignature = - BuildSceneViewEditorOverlayContentSignature(scene, selectedObjectIds); + BuildSceneViewportOverlayContentSignature(scene, selectedObjectIds); if (!ShouldRebuildSceneViewEditorOverlayFrameData( scene, overlay, @@ -581,10 +447,10 @@ private: return; } - m_sceneViewEditorOverlayFrameData = {}; - m_sceneViewEditorOverlayFrameData.overlay = overlay; + SceneViewportOverlayFrameData frameData = {}; + frameData.overlay = overlay; if (scene != nullptr && overlay.valid && viewportWidth > 0u && viewportHeight > 0u) { - m_sceneViewEditorOverlayFrameData = m_sceneViewportOverlayBuilder.Build( + frameData = m_sceneViewportOverlayBuilder.Build( context, overlay, viewportWidth, @@ -593,12 +459,14 @@ private: &m_sceneViewTransformGizmoOverlayState); } - m_sceneViewEditorOverlayScene = scene; - m_sceneViewEditorOverlaySelectedObjectIds = selectedObjectIds; - m_sceneViewEditorOverlayViewportWidth = viewportWidth; - m_sceneViewEditorOverlayViewportHeight = viewportHeight; - m_sceneViewEditorOverlayContentSignature = contentSignature; - m_sceneViewEditorOverlayCached = true; + UpdateSceneViewportOverlayFrameCacheState( + m_sceneViewEditorOverlayFrameCache, + scene, + viewportWidth, + viewportHeight, + selectedObjectIds, + contentSignature, + frameData); m_sceneViewTransformGizmoOverlayDirty = false; } @@ -861,14 +729,8 @@ private: Rendering::RenderContext m_sceneViewLastRenderContext = {}; std::array m_entries = {}; SceneViewCameraState m_sceneViewCamera; - SceneViewportOverlayFrameData m_sceneViewEditorOverlayFrameData = {}; SceneViewportTransformGizmoOverlayState m_sceneViewTransformGizmoOverlayState = {}; - const Components::Scene* m_sceneViewEditorOverlayScene = nullptr; - std::vector m_sceneViewEditorOverlaySelectedObjectIds = {}; - uint32_t m_sceneViewEditorOverlayViewportWidth = 0u; - uint32_t m_sceneViewEditorOverlayViewportHeight = 0u; - uint64_t m_sceneViewEditorOverlayContentSignature = 0u; - bool m_sceneViewEditorOverlayCached = false; + SceneViewportOverlayFrameCacheState m_sceneViewEditorOverlayFrameCache = {}; bool m_sceneViewTransformGizmoOverlayDirty = false; SceneViewportGridPassRenderer m_sceneViewportGridRenderer; SceneViewportSelectionOutlinePassRenderer m_sceneViewportSelectionOutlineRenderer; diff --git a/tests/editor/CMakeLists.txt b/tests/editor/CMakeLists.txt index 80610753..10b8ce54 100644 --- a/tests/editor/CMakeLists.txt +++ b/tests/editor/CMakeLists.txt @@ -18,6 +18,7 @@ set(EDITOR_TEST_SOURCES test_scene_viewport_chrome.cpp test_scene_viewport_transform_gizmo_coordinator.cpp test_scene_viewport_shader_paths.cpp + test_scene_viewport_overlay_frame_cache.cpp test_scene_viewport_overlay_sprite_resources.cpp test_scene_viewport_overlay_renderer.cpp test_scene_viewport_overlay_providers.cpp @@ -50,6 +51,7 @@ set(EDITOR_TEST_SOURCES ${CMAKE_SOURCE_DIR}/editor/src/Viewport/SceneViewportTransformGizmoCoordinator.cpp ${CMAKE_SOURCE_DIR}/editor/src/Viewport/SceneViewportOrientationGizmo.cpp ${CMAKE_SOURCE_DIR}/editor/src/Viewport/SceneViewportOverlayBuilder.cpp + ${CMAKE_SOURCE_DIR}/editor/src/Viewport/SceneViewportOverlayFrameCache.cpp ${CMAKE_SOURCE_DIR}/editor/src/Viewport/SceneViewportOverlaySpriteResources.cpp ${CMAKE_SOURCE_DIR}/editor/src/Viewport/SceneViewportOverlayProviders.cpp ) diff --git a/tests/editor/test_scene_viewport_overlay_frame_cache.cpp b/tests/editor/test_scene_viewport_overlay_frame_cache.cpp new file mode 100644 index 00000000..42cc63af --- /dev/null +++ b/tests/editor/test_scene_viewport_overlay_frame_cache.cpp @@ -0,0 +1,162 @@ +#include + +#include "Core/EditorContext.h" +#include "Viewport/SceneViewportOverlayFrameCache.h" + +#include +#include + +namespace XCEngine::Editor { +namespace { + +SceneViewportOverlayData CreateValidOverlay() { + SceneViewportOverlayData overlay = {}; + overlay.valid = true; + overlay.cameraPosition = Math::Vector3(1.0f, 2.0f, 3.0f); + overlay.cameraForward = Math::Vector3::Forward(); + overlay.cameraRight = Math::Vector3::Right(); + overlay.cameraUp = Math::Vector3::Up(); + overlay.verticalFovDegrees = 60.0f; + overlay.nearClipPlane = 0.1f; + overlay.farClipPlane = 1000.0f; + overlay.orbitDistance = 6.0f; + return overlay; +} + +TEST(SceneViewportOverlayFrameCacheTest, ResolveViewportSizePrefersRequestedDimensions) { + uint32_t width = 0u; + uint32_t height = 0u; + + ResolveSceneViewportOverlayFrameViewportSize( + 1280u, + 720u, + 960u, + 540u, + width, + height); + + EXPECT_EQ(width, 1280u); + EXPECT_EQ(height, 720u); + + ResolveSceneViewportOverlayFrameViewportSize( + 0u, + 0u, + 960u, + 540u, + width, + height); + + EXPECT_EQ(width, 960u); + EXPECT_EQ(height, 540u); +} + +TEST(SceneViewportOverlayFrameCacheTest, OverlayDataComparisonUsesEpsilonTolerance) { + const SceneViewportOverlayData lhs = CreateValidOverlay(); + SceneViewportOverlayData rhs = lhs; + rhs.cameraPosition.x += 5e-5f; + rhs.orbitDistance += 5e-5f; + + EXPECT_TRUE(AreEqualSceneViewportOverlayData(lhs, rhs)); + + rhs.cameraPosition.x += 1e-3f; + EXPECT_FALSE(AreEqualSceneViewportOverlayData(lhs, rhs)); +} + +TEST(SceneViewportOverlayFrameCacheTest, RebuildDecisionTracksCacheMetadataAndDirtyState) { + SceneViewportOverlayFrameCacheState cacheState = {}; + const SceneViewportOverlayData overlay = CreateValidOverlay(); + const std::vector selectedObjectIds = { 3u, 7u }; + const uint64_t contentSignature = 19u; + + EXPECT_TRUE(ShouldRebuildSceneViewportOverlayFrameCache( + cacheState, + nullptr, + overlay, + 1280u, + 720u, + selectedObjectIds, + contentSignature, + false)); + + SceneViewportOverlayFrameData frameData = {}; + frameData.overlay = overlay; + UpdateSceneViewportOverlayFrameCacheState( + cacheState, + nullptr, + 1280u, + 720u, + selectedObjectIds, + contentSignature, + frameData); + + EXPECT_TRUE(cacheState.cached); + EXPECT_EQ(cacheState.viewportWidth, 1280u); + EXPECT_EQ(cacheState.viewportHeight, 720u); + EXPECT_EQ(cacheState.selectedObjectIds, selectedObjectIds); + EXPECT_FALSE(ShouldRebuildSceneViewportOverlayFrameCache( + cacheState, + nullptr, + overlay, + 1280u, + 720u, + selectedObjectIds, + contentSignature, + false)); + EXPECT_TRUE(ShouldRebuildSceneViewportOverlayFrameCache( + cacheState, + nullptr, + overlay, + 1280u, + 720u, + selectedObjectIds, + contentSignature, + true)); + EXPECT_TRUE(ShouldRebuildSceneViewportOverlayFrameCache( + cacheState, + nullptr, + overlay, + 1920u, + 1080u, + selectedObjectIds, + contentSignature, + false)); +} + +TEST(SceneViewportOverlayFrameCacheTest, ContentSignatureTracksSceneCameraAndLightState) { + EditorContext context; + context.GetSceneManager().NewScene("Overlay Frame Cache"); + + auto* cameraEntity = context.GetSceneManager().CreateEntity("SceneCamera"); + ASSERT_NE(cameraEntity, nullptr); + auto* camera = cameraEntity->AddComponent(); + ASSERT_NE(camera, nullptr); + camera->SetNearClipPlane(0.3f); + cameraEntity->GetTransform()->SetPosition(Math::Vector3(0.0f, 1.0f, 5.0f)); + + auto* lightEntity = context.GetSceneManager().CreateEntity("DirectionalLight"); + ASSERT_NE(lightEntity, nullptr); + auto* light = lightEntity->AddComponent(); + ASSERT_NE(light, nullptr); + light->SetLightType(Components::LightType::Directional); + + const std::vector selectedObjectIds = { cameraEntity->GetID() }; + const Components::Scene* scene = context.GetSceneManager().GetScene(); + ASSERT_NE(scene, nullptr); + + const uint64_t initialSignature = + BuildSceneViewportOverlayContentSignature(scene, selectedObjectIds); + + light->SetLightType(Components::LightType::Point); + const uint64_t changedLightSignature = + BuildSceneViewportOverlayContentSignature(scene, selectedObjectIds); + EXPECT_NE(initialSignature, changedLightSignature); + + light->SetLightType(Components::LightType::Directional); + cameraEntity->GetTransform()->SetPosition(Math::Vector3(2.0f, 1.0f, 5.0f)); + const uint64_t movedCameraSignature = + BuildSceneViewportOverlayContentSignature(scene, selectedObjectIds); + EXPECT_NE(initialSignature, movedCameraSignature); +} + +} // namespace +} // namespace XCEngine::Editor