Extract scene viewport overlay frame cache module

This commit is contained in:
2026-04-04 17:31:39 +08:00
parent c6b835a390
commit a833a95216
6 changed files with 462 additions and 171 deletions

View File

@@ -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

View File

@@ -0,0 +1,200 @@
#include "Viewport/SceneViewportOverlayFrameCache.h"
#include <XCEngine/Components/CameraComponent.h>
#include <XCEngine/Components/GameObject.h>
#include <XCEngine/Components/LightComponent.h>
#include <XCEngine/Components/TransformComponent.h>
#include <XCEngine/Scene/Scene.h>
#include <cmath>
#include <cstring>
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<const uint8_t*>(data);
for (size_t index = 0; index < size; ++index) {
hash ^= static_cast<uint64_t>(bytes[index]);
hash *= kSceneViewportOverlaySignaturePrime;
}
}
template <typename TValue>
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<uint64_t>& selectedObjectIds) {
uint64_t hash = kSceneViewportOverlaySignatureOffsetBasis;
HashSceneViewportOverlayValue(hash, static_cast<uint64_t>(selectedObjectIds.size()));
for (uint64_t entityId : selectedObjectIds) {
HashSceneViewportOverlayValue(hash, entityId);
}
if (scene == nullptr) {
return hash;
}
for (Components::CameraComponent* camera : scene->FindObjectsOfType<Components::CameraComponent>()) {
Components::GameObject* gameObject = camera != nullptr ? camera->GetGameObject() : nullptr;
HashSceneViewportOverlayValue(hash, static_cast<uint8_t>(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<uint32_t>(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::LightComponent>()) {
Components::GameObject* gameObject = light != nullptr ? light->GetGameObject() : nullptr;
HashSceneViewportOverlayValue(hash, static_cast<uint8_t>(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<uint32_t>(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<uint64_t>& 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<uint64_t>& 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

View File

@@ -0,0 +1,64 @@
#pragma once
#include "SceneViewportEditorOverlayData.h"
#include <cstdint>
#include <vector>
namespace XCEngine {
namespace Components {
class Scene;
} // namespace Components
namespace Editor {
struct SceneViewportOverlayFrameCacheState {
SceneViewportOverlayFrameData frameData = {};
const Components::Scene* scene = nullptr;
std::vector<uint64_t> 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<uint64_t>& selectedObjectIds);
bool ShouldRebuildSceneViewportOverlayFrameCache(
const SceneViewportOverlayFrameCacheState& cacheState,
const Components::Scene* scene,
const SceneViewportOverlayData& overlay,
uint32_t viewportWidth,
uint32_t viewportHeight,
const std::vector<uint64_t>& selectedObjectIds,
uint64_t contentSignature,
bool transformGizmoOverlayDirty);
void UpdateSceneViewportOverlayFrameCacheState(
SceneViewportOverlayFrameCacheState& cacheState,
const Components::Scene* scene,
uint32_t viewportWidth,
uint32_t viewportHeight,
const std::vector<uint64_t>& selectedObjectIds,
uint64_t contentSignature,
const SceneViewportOverlayFrameData& frameData);
} // namespace Editor
} // namespace XCEngine

View File

@@ -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 <XCEngine/Components/CameraComponent.h>
#include <XCEngine/Components/GameObject.h>
#include <XCEngine/Components/LightComponent.h>
#include <XCEngine/Core/Asset/ResourceManager.h>
#include <XCEngine/RHI/RHIDevice.h>
#include <XCEngine/RHI/RHIEnums.h>
#include <XCEngine/RHI/RHIResourceView.h>
#include <XCEngine/RHI/RHITexture.h>
#include <XCEngine/Rendering/Passes/BuiltinInfiniteGridPass.h>
#include <XCEngine/Rendering/RenderContext.h>
#include <XCEngine/Rendering/RenderSurface.h>
#include <XCEngine/Rendering/SceneRenderer.h>
#include <XCEngine/Scene/Scene.h>
#include <array>
#include <cmath>
#include <cstdint>
#include <cstring>
#include <memory>
#include <string>
#include <vector>
@@ -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<const uint8_t*>(data);
for (size_t index = 0; index < size; ++index) {
hash ^= static_cast<uint64_t>(bytes[index]);
hash *= kSceneViewportOverlaySignaturePrime;
}
}
template <typename TValue>
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<uint64_t>& selectedObjectIds) {
uint64_t hash = kSceneViewportOverlaySignatureOffsetBasis;
HashSceneViewportOverlayValue(hash, static_cast<uint64_t>(selectedObjectIds.size()));
for (uint64_t entityId : selectedObjectIds) {
HashSceneViewportOverlayValue(hash, entityId);
}
if (scene == nullptr) {
return hash;
}
for (Components::CameraComponent* camera : scene->FindObjectsOfType<Components::CameraComponent>()) {
Components::GameObject* gameObject = camera != nullptr ? camera->GetGameObject() : nullptr;
HashSceneViewportOverlayValue(hash, static_cast<uint8_t>(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<uint32_t>(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::LightComponent>()) {
Components::GameObject* gameObject = light != nullptr ? light->GetGameObject() : nullptr;
HashSceneViewportOverlayValue(hash, static_cast<uint8_t>(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<uint32_t>(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<uint64_t>& 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<uint64_t> 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<ViewportEntry, 2> m_entries = {};
SceneViewCameraState m_sceneViewCamera;
SceneViewportOverlayFrameData m_sceneViewEditorOverlayFrameData = {};
SceneViewportTransformGizmoOverlayState m_sceneViewTransformGizmoOverlayState = {};
const Components::Scene* m_sceneViewEditorOverlayScene = nullptr;
std::vector<uint64_t> 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;

View File

@@ -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
)

View File

@@ -0,0 +1,162 @@
#include <gtest/gtest.h>
#include "Core/EditorContext.h"
#include "Viewport/SceneViewportOverlayFrameCache.h"
#include <XCEngine/Components/CameraComponent.h>
#include <XCEngine/Components/LightComponent.h>
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<uint64_t> 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<Components::CameraComponent>();
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<Components::LightComponent>();
ASSERT_NE(light, nullptr);
light->SetLightType(Components::LightType::Directional);
const std::vector<uint64_t> 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