refactor: extract viewport host surface utilities
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
#include "IViewportHostService.h"
|
||||
#include "SceneViewportPicker.h"
|
||||
#include "SceneViewportCameraController.h"
|
||||
#include "ViewportHostSurfaceUtils.h"
|
||||
#include "UI/ImGuiBackendBridge.h"
|
||||
|
||||
#include <XCEngine/Components/CameraComponent.h>
|
||||
@@ -75,16 +76,6 @@ inline void SetViewportStatusIfEmpty(std::string& statusText, const char* messag
|
||||
}
|
||||
}
|
||||
|
||||
inline uint32_t ClampViewportPixelCoordinate(float value, uint32_t extent) {
|
||||
if (extent == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const float maxCoordinate = static_cast<float>(extent - 1u);
|
||||
const float clamped = (std::max)(0.0f, (std::min)(value, maxCoordinate));
|
||||
return static_cast<uint32_t>(std::floor(clamped));
|
||||
}
|
||||
|
||||
Rendering::Passes::InfiniteGridPassData BuildInfiniteGridPassData(
|
||||
const SceneViewportOverlayData& overlay) {
|
||||
Rendering::Passes::InfiniteGridPassData data = {};
|
||||
@@ -422,28 +413,6 @@ private:
|
||||
m_sceneViewCamera.controller.Focus(center);
|
||||
}
|
||||
|
||||
RHI::TextureDesc BuildViewportTextureDesc(uint32_t width, uint32_t height, RHI::Format format) const {
|
||||
RHI::TextureDesc desc = {};
|
||||
desc.width = width;
|
||||
desc.height = height;
|
||||
desc.depth = 1;
|
||||
desc.mipLevels = 1;
|
||||
desc.arraySize = 1;
|
||||
desc.format = static_cast<uint32_t>(format);
|
||||
desc.textureType = static_cast<uint32_t>(RHI::TextureType::Texture2D);
|
||||
desc.sampleCount = 1;
|
||||
desc.sampleQuality = 0;
|
||||
desc.flags = 0;
|
||||
return desc;
|
||||
}
|
||||
|
||||
RHI::ResourceViewDesc BuildViewportTextureViewDesc(RHI::Format format) const {
|
||||
RHI::ResourceViewDesc desc = {};
|
||||
desc.format = static_cast<uint32_t>(format);
|
||||
desc.dimension = RHI::ResourceViewDimension::Texture2D;
|
||||
return desc;
|
||||
}
|
||||
|
||||
bool CreateViewportColorResources(ViewportEntry& entry) {
|
||||
const RHI::TextureDesc colorDesc =
|
||||
BuildViewportTextureDesc(entry.width, entry.height, RHI::Format::R8G8B8A8_UNorm);
|
||||
@@ -505,22 +474,26 @@ private:
|
||||
}
|
||||
|
||||
bool EnsureViewportResources(ViewportEntry& entry) {
|
||||
if (entry.requestedWidth == 0 || entry.requestedHeight == 0) {
|
||||
return false;
|
||||
ViewportHostResourceReuseQuery reuseQuery = {};
|
||||
reuseQuery.kind = entry.kind;
|
||||
reuseQuery.width = entry.width;
|
||||
reuseQuery.height = entry.height;
|
||||
reuseQuery.requestedWidth = entry.requestedWidth;
|
||||
reuseQuery.requestedHeight = entry.requestedHeight;
|
||||
reuseQuery.resources.hasColorTexture = entry.colorTexture != nullptr;
|
||||
reuseQuery.resources.hasColorView = entry.colorView != nullptr;
|
||||
reuseQuery.resources.hasDepthTexture = entry.depthTexture != nullptr;
|
||||
reuseQuery.resources.hasDepthView = entry.depthView != nullptr;
|
||||
reuseQuery.resources.hasObjectIdTexture = entry.objectIdTexture != nullptr;
|
||||
reuseQuery.resources.hasObjectIdView = entry.objectIdView != nullptr;
|
||||
reuseQuery.resources.hasObjectIdShaderView = entry.objectIdShaderView != nullptr;
|
||||
reuseQuery.resources.hasTextureDescriptor = entry.textureId != ImTextureID{};
|
||||
if (CanReuseViewportResources(reuseQuery)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (entry.width == entry.requestedWidth &&
|
||||
entry.height == entry.requestedHeight &&
|
||||
entry.colorTexture != nullptr &&
|
||||
entry.colorView != nullptr &&
|
||||
entry.depthTexture != nullptr &&
|
||||
entry.depthView != nullptr &&
|
||||
(entry.kind != EditorViewportKind::Scene ||
|
||||
(entry.objectIdTexture != nullptr &&
|
||||
entry.objectIdView != nullptr &&
|
||||
entry.objectIdShaderView != nullptr)) &&
|
||||
entry.textureId != ImTextureID{}) {
|
||||
return true;
|
||||
if (entry.requestedWidth == 0 || entry.requestedHeight == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DestroyViewportResources(entry);
|
||||
@@ -538,7 +511,7 @@ private:
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entry.kind == EditorViewportKind::Scene &&
|
||||
if (ViewportRequiresObjectIdResources(entry.kind) &&
|
||||
!CreateSceneViewportObjectIdResources(entry)) {
|
||||
DestroyViewportResources(entry);
|
||||
return false;
|
||||
@@ -555,21 +528,21 @@ private:
|
||||
}
|
||||
|
||||
Rendering::RenderSurface BuildSurface(const ViewportEntry& entry) const {
|
||||
Rendering::RenderSurface surface(entry.width, entry.height);
|
||||
surface.SetColorAttachment(entry.colorView);
|
||||
surface.SetDepthAttachment(entry.depthView);
|
||||
surface.SetColorStateBefore(entry.colorState);
|
||||
surface.SetColorStateAfter(RHI::ResourceStates::PixelShaderResource);
|
||||
return surface;
|
||||
return BuildViewportRenderSurface(
|
||||
entry.width,
|
||||
entry.height,
|
||||
entry.colorView,
|
||||
entry.depthView,
|
||||
entry.colorState);
|
||||
}
|
||||
|
||||
Rendering::RenderSurface BuildObjectIdSurface(const ViewportEntry& entry) const {
|
||||
Rendering::RenderSurface surface(entry.width, entry.height);
|
||||
surface.SetColorAttachment(entry.objectIdView);
|
||||
surface.SetDepthAttachment(entry.depthView);
|
||||
surface.SetColorStateBefore(entry.objectIdState);
|
||||
surface.SetColorStateAfter(RHI::ResourceStates::PixelShaderResource);
|
||||
return surface;
|
||||
return BuildViewportRenderSurface(
|
||||
entry.width,
|
||||
entry.height,
|
||||
entry.objectIdView,
|
||||
entry.depthView,
|
||||
entry.objectIdState);
|
||||
}
|
||||
|
||||
void AddSceneColorToRenderTargetPass(
|
||||
|
||||
118
editor/src/Viewport/ViewportHostSurfaceUtils.h
Normal file
118
editor/src/Viewport/ViewportHostSurfaceUtils.h
Normal file
@@ -0,0 +1,118 @@
|
||||
#pragma once
|
||||
|
||||
#include "IViewportHostService.h"
|
||||
|
||||
#include <XCEngine/RHI/RHIEnums.h>
|
||||
#include <XCEngine/RHI/RHIResourceView.h>
|
||||
#include <XCEngine/RHI/RHITypes.h>
|
||||
#include <XCEngine/Rendering/RenderSurface.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
|
||||
struct ViewportHostResourcePresence {
|
||||
bool hasColorTexture = false;
|
||||
bool hasColorView = false;
|
||||
bool hasDepthTexture = false;
|
||||
bool hasDepthView = false;
|
||||
bool hasObjectIdTexture = false;
|
||||
bool hasObjectIdView = false;
|
||||
bool hasObjectIdShaderView = false;
|
||||
bool hasTextureDescriptor = false;
|
||||
};
|
||||
|
||||
struct ViewportHostResourceReuseQuery {
|
||||
EditorViewportKind kind = EditorViewportKind::Scene;
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
uint32_t requestedWidth = 0;
|
||||
uint32_t requestedHeight = 0;
|
||||
ViewportHostResourcePresence resources = {};
|
||||
};
|
||||
|
||||
inline bool ViewportRequiresObjectIdResources(EditorViewportKind kind) {
|
||||
return kind == EditorViewportKind::Scene;
|
||||
}
|
||||
|
||||
inline uint32_t ClampViewportPixelCoordinate(float value, uint32_t extent) {
|
||||
if (extent == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const float maxCoordinate = static_cast<float>(extent - 1u);
|
||||
const float clamped = (std::max)(0.0f, (std::min)(value, maxCoordinate));
|
||||
return static_cast<uint32_t>(std::floor(clamped));
|
||||
}
|
||||
|
||||
inline bool CanReuseViewportResources(const ViewportHostResourceReuseQuery& query) {
|
||||
if (query.requestedWidth == 0 || query.requestedHeight == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (query.width != query.requestedWidth || query.height != query.requestedHeight) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!query.resources.hasColorTexture ||
|
||||
!query.resources.hasColorView ||
|
||||
!query.resources.hasDepthTexture ||
|
||||
!query.resources.hasDepthView ||
|
||||
!query.resources.hasTextureDescriptor) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ViewportRequiresObjectIdResources(query.kind)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return query.resources.hasObjectIdTexture &&
|
||||
query.resources.hasObjectIdView &&
|
||||
query.resources.hasObjectIdShaderView;
|
||||
}
|
||||
|
||||
inline RHI::TextureDesc BuildViewportTextureDesc(
|
||||
uint32_t width,
|
||||
uint32_t height,
|
||||
RHI::Format format) {
|
||||
RHI::TextureDesc desc = {};
|
||||
desc.width = width;
|
||||
desc.height = height;
|
||||
desc.depth = 1;
|
||||
desc.mipLevels = 1;
|
||||
desc.arraySize = 1;
|
||||
desc.format = static_cast<uint32_t>(format);
|
||||
desc.textureType = static_cast<uint32_t>(RHI::TextureType::Texture2D);
|
||||
desc.sampleCount = 1;
|
||||
desc.sampleQuality = 0;
|
||||
desc.flags = 0;
|
||||
return desc;
|
||||
}
|
||||
|
||||
inline RHI::ResourceViewDesc BuildViewportTextureViewDesc(RHI::Format format) {
|
||||
RHI::ResourceViewDesc desc = {};
|
||||
desc.format = static_cast<uint32_t>(format);
|
||||
desc.dimension = RHI::ResourceViewDimension::Texture2D;
|
||||
return desc;
|
||||
}
|
||||
|
||||
inline Rendering::RenderSurface BuildViewportRenderSurface(
|
||||
uint32_t width,
|
||||
uint32_t height,
|
||||
RHI::RHIResourceView* colorView,
|
||||
RHI::RHIResourceView* depthView,
|
||||
RHI::ResourceStates colorStateBefore,
|
||||
RHI::ResourceStates colorStateAfter = RHI::ResourceStates::PixelShaderResource) {
|
||||
Rendering::RenderSurface surface(width, height);
|
||||
surface.SetColorAttachment(colorView);
|
||||
surface.SetDepthAttachment(depthView);
|
||||
surface.SetColorStateBefore(colorStateBefore);
|
||||
surface.SetColorStateAfter(colorStateAfter);
|
||||
return surface;
|
||||
}
|
||||
|
||||
} // namespace Editor
|
||||
} // namespace XCEngine
|
||||
@@ -10,6 +10,7 @@ set(EDITOR_TEST_SOURCES
|
||||
test_scene_viewport_scale_gizmo.cpp
|
||||
test_scene_viewport_picker.cpp
|
||||
test_scene_viewport_overlay_renderer.cpp
|
||||
test_viewport_host_surface_utils.cpp
|
||||
${CMAKE_SOURCE_DIR}/editor/src/Core/UndoManager.cpp
|
||||
${CMAKE_SOURCE_DIR}/editor/src/Managers/SceneManager.cpp
|
||||
${CMAKE_SOURCE_DIR}/editor/src/Managers/ProjectManager.cpp
|
||||
|
||||
151
tests/editor/test_viewport_host_surface_utils.cpp
Normal file
151
tests/editor/test_viewport_host_surface_utils.cpp
Normal file
@@ -0,0 +1,151 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "Viewport/ViewportHostSurfaceUtils.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using XCEngine::Editor::BuildViewportRenderSurface;
|
||||
using XCEngine::Editor::BuildViewportTextureDesc;
|
||||
using XCEngine::Editor::BuildViewportTextureViewDesc;
|
||||
using XCEngine::Editor::CanReuseViewportResources;
|
||||
using XCEngine::Editor::ClampViewportPixelCoordinate;
|
||||
using XCEngine::Editor::EditorViewportKind;
|
||||
using XCEngine::Editor::ViewportHostResourceReuseQuery;
|
||||
using XCEngine::Editor::ViewportRequiresObjectIdResources;
|
||||
using XCEngine::RHI::Format;
|
||||
using XCEngine::RHI::RHIResourceView;
|
||||
using XCEngine::RHI::ResourceStates;
|
||||
using XCEngine::RHI::ResourceViewDimension;
|
||||
using XCEngine::RHI::ResourceViewType;
|
||||
|
||||
class DummyResourceView final : public RHIResourceView {
|
||||
public:
|
||||
void Shutdown() override {
|
||||
}
|
||||
|
||||
void* GetNativeHandle() override {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool IsValid() const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
ResourceViewType GetViewType() const override {
|
||||
return ResourceViewType::RenderTarget;
|
||||
}
|
||||
|
||||
ResourceViewDimension GetDimension() const override {
|
||||
return ResourceViewDimension::Texture2D;
|
||||
}
|
||||
|
||||
Format GetFormat() const override {
|
||||
return Format::R8G8B8A8_UNorm;
|
||||
}
|
||||
};
|
||||
|
||||
TEST(ViewportHostSurfaceUtilsTest, ClampViewportPixelCoordinateClampsAndFloors) {
|
||||
EXPECT_EQ(ClampViewportPixelCoordinate(-3.0f, 1280), 0u);
|
||||
EXPECT_EQ(ClampViewportPixelCoordinate(0.99f, 1280), 0u);
|
||||
EXPECT_EQ(ClampViewportPixelCoordinate(10.8f, 1280), 10u);
|
||||
EXPECT_EQ(ClampViewportPixelCoordinate(5000.0f, 1280), 1279u);
|
||||
EXPECT_EQ(ClampViewportPixelCoordinate(12.0f, 0), 0u);
|
||||
}
|
||||
|
||||
TEST(ViewportHostSurfaceUtilsTest, ViewportReuseRequiresObjectIdOnlyForSceneViewport) {
|
||||
ViewportHostResourceReuseQuery gameQuery = {};
|
||||
gameQuery.kind = EditorViewportKind::Game;
|
||||
gameQuery.width = 1280;
|
||||
gameQuery.height = 720;
|
||||
gameQuery.requestedWidth = 1280;
|
||||
gameQuery.requestedHeight = 720;
|
||||
gameQuery.resources.hasColorTexture = true;
|
||||
gameQuery.resources.hasColorView = true;
|
||||
gameQuery.resources.hasDepthTexture = true;
|
||||
gameQuery.resources.hasDepthView = true;
|
||||
gameQuery.resources.hasTextureDescriptor = true;
|
||||
|
||||
EXPECT_FALSE(ViewportRequiresObjectIdResources(EditorViewportKind::Game));
|
||||
EXPECT_TRUE(CanReuseViewportResources(gameQuery));
|
||||
|
||||
ViewportHostResourceReuseQuery sceneQuery = gameQuery;
|
||||
sceneQuery.kind = EditorViewportKind::Scene;
|
||||
EXPECT_TRUE(ViewportRequiresObjectIdResources(EditorViewportKind::Scene));
|
||||
EXPECT_FALSE(CanReuseViewportResources(sceneQuery));
|
||||
|
||||
sceneQuery.resources.hasObjectIdTexture = true;
|
||||
sceneQuery.resources.hasObjectIdView = true;
|
||||
sceneQuery.resources.hasObjectIdShaderView = true;
|
||||
EXPECT_TRUE(CanReuseViewportResources(sceneQuery));
|
||||
}
|
||||
|
||||
TEST(ViewportHostSurfaceUtilsTest, ViewportReuseRejectsMismatchedOrMissingResources) {
|
||||
ViewportHostResourceReuseQuery query = {};
|
||||
query.kind = EditorViewportKind::Scene;
|
||||
query.width = 1280;
|
||||
query.height = 720;
|
||||
query.requestedWidth = 1280;
|
||||
query.requestedHeight = 720;
|
||||
query.resources.hasColorTexture = true;
|
||||
query.resources.hasColorView = true;
|
||||
query.resources.hasDepthTexture = true;
|
||||
query.resources.hasDepthView = true;
|
||||
query.resources.hasObjectIdTexture = true;
|
||||
query.resources.hasObjectIdView = true;
|
||||
query.resources.hasObjectIdShaderView = true;
|
||||
query.resources.hasTextureDescriptor = true;
|
||||
|
||||
EXPECT_TRUE(CanReuseViewportResources(query));
|
||||
|
||||
query.requestedWidth = 0;
|
||||
EXPECT_FALSE(CanReuseViewportResources(query));
|
||||
|
||||
query.requestedWidth = 1280;
|
||||
query.width = 1024;
|
||||
EXPECT_FALSE(CanReuseViewportResources(query));
|
||||
|
||||
query.width = 1280;
|
||||
query.resources.hasTextureDescriptor = false;
|
||||
EXPECT_FALSE(CanReuseViewportResources(query));
|
||||
}
|
||||
|
||||
TEST(ViewportHostSurfaceUtilsTest, BuildViewportTextureDescriptorsUseExpectedDefaults) {
|
||||
const auto textureDesc = BuildViewportTextureDesc(640, 360, Format::D24_UNorm_S8_UInt);
|
||||
EXPECT_EQ(textureDesc.width, 640u);
|
||||
EXPECT_EQ(textureDesc.height, 360u);
|
||||
EXPECT_EQ(textureDesc.depth, 1u);
|
||||
EXPECT_EQ(textureDesc.mipLevels, 1u);
|
||||
EXPECT_EQ(textureDesc.arraySize, 1u);
|
||||
EXPECT_EQ(textureDesc.format, static_cast<uint32_t>(Format::D24_UNorm_S8_UInt));
|
||||
EXPECT_EQ(textureDesc.textureType, static_cast<uint32_t>(XCEngine::RHI::TextureType::Texture2D));
|
||||
EXPECT_EQ(textureDesc.sampleCount, 1u);
|
||||
EXPECT_EQ(textureDesc.sampleQuality, 0u);
|
||||
EXPECT_EQ(textureDesc.flags, 0u);
|
||||
|
||||
const auto viewDesc = BuildViewportTextureViewDesc(Format::R8G8B8A8_UNorm);
|
||||
EXPECT_EQ(viewDesc.format, static_cast<uint32_t>(Format::R8G8B8A8_UNorm));
|
||||
EXPECT_EQ(viewDesc.dimension, ResourceViewDimension::Texture2D);
|
||||
}
|
||||
|
||||
TEST(ViewportHostSurfaceUtilsTest, BuildViewportRenderSurfaceCarriesAttachmentsAndStates) {
|
||||
DummyResourceView colorView;
|
||||
DummyResourceView depthView;
|
||||
|
||||
const auto surface = BuildViewportRenderSurface(
|
||||
800,
|
||||
600,
|
||||
&colorView,
|
||||
&depthView,
|
||||
ResourceStates::Common,
|
||||
ResourceStates::PixelShaderResource);
|
||||
|
||||
ASSERT_EQ(surface.GetWidth(), 800u);
|
||||
ASSERT_EQ(surface.GetHeight(), 600u);
|
||||
ASSERT_EQ(surface.GetColorAttachments().size(), 1u);
|
||||
EXPECT_EQ(surface.GetColorAttachments()[0], &colorView);
|
||||
EXPECT_EQ(surface.GetDepthAttachment(), &depthView);
|
||||
EXPECT_EQ(surface.GetColorStateBefore(), ResourceStates::Common);
|
||||
EXPECT_EQ(surface.GetColorStateAfter(), ResourceStates::PixelShaderResource);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
Reference in New Issue
Block a user