refactor: extract viewport render targets

This commit is contained in:
2026-04-01 21:01:23 +08:00
parent 1af958a272
commit 1fbbf318de
4 changed files with 450 additions and 223 deletions

View File

@@ -0,0 +1,211 @@
#pragma once
#include "ViewportHostSurfaceUtils.h"
#include "UI/ImGuiBackendBridge.h"
#include <XCEngine/RHI/RHIDevice.h>
#include <XCEngine/RHI/RHIResourceView.h>
#include <XCEngine/RHI/RHITexture.h>
namespace XCEngine {
namespace Editor {
struct ViewportRenderTargets {
uint32_t width = 0;
uint32_t height = 0;
RHI::RHITexture* colorTexture = nullptr;
RHI::RHIResourceView* colorView = nullptr;
RHI::RHITexture* depthTexture = nullptr;
RHI::RHIResourceView* depthView = nullptr;
RHI::RHITexture* objectIdTexture = nullptr;
RHI::RHIResourceView* objectIdView = nullptr;
RHI::RHIResourceView* objectIdShaderView = nullptr;
D3D12_CPU_DESCRIPTOR_HANDLE imguiCpuHandle = {};
D3D12_GPU_DESCRIPTOR_HANDLE imguiGpuHandle = {};
ImTextureID textureId = {};
RHI::ResourceStates colorState = RHI::ResourceStates::Common;
RHI::ResourceStates objectIdState = RHI::ResourceStates::Common;
bool hasValidObjectIdFrame = false;
};
inline ViewportHostResourceReuseQuery BuildViewportRenderTargetsReuseQuery(
EditorViewportKind kind,
const ViewportRenderTargets& targets,
uint32_t requestedWidth,
uint32_t requestedHeight) {
ViewportHostResourceReuseQuery query = {};
query.kind = kind;
query.width = targets.width;
query.height = targets.height;
query.requestedWidth = requestedWidth;
query.requestedHeight = requestedHeight;
query.resources.hasColorTexture = targets.colorTexture != nullptr;
query.resources.hasColorView = targets.colorView != nullptr;
query.resources.hasDepthTexture = targets.depthTexture != nullptr;
query.resources.hasDepthView = targets.depthView != nullptr;
query.resources.hasObjectIdTexture = targets.objectIdTexture != nullptr;
query.resources.hasObjectIdView = targets.objectIdView != nullptr;
query.resources.hasObjectIdShaderView = targets.objectIdShaderView != nullptr;
query.resources.hasTextureDescriptor = targets.textureId != ImTextureID{};
return query;
}
inline Rendering::RenderSurface BuildViewportColorSurface(const ViewportRenderTargets& targets) {
return BuildViewportRenderSurface(
targets.width,
targets.height,
targets.colorView,
targets.depthView,
targets.colorState);
}
inline Rendering::RenderSurface BuildViewportObjectIdSurface(const ViewportRenderTargets& targets) {
return BuildViewportRenderSurface(
targets.width,
targets.height,
targets.objectIdView,
targets.depthView,
targets.objectIdState);
}
namespace Detail {
template <typename ResourceType>
inline void ShutdownAndDelete(ResourceType*& resource) {
if (resource == nullptr) {
return;
}
resource->Shutdown();
delete resource;
resource = nullptr;
}
inline bool CreateViewportColorResources(
RHI::RHIDevice* device,
ViewportRenderTargets& targets) {
const RHI::TextureDesc colorDesc =
BuildViewportTextureDesc(targets.width, targets.height, RHI::Format::R8G8B8A8_UNorm);
targets.colorTexture = device->CreateTexture(colorDesc);
if (targets.colorTexture == nullptr) {
return false;
}
const RHI::ResourceViewDesc colorViewDesc =
BuildViewportTextureViewDesc(RHI::Format::R8G8B8A8_UNorm);
targets.colorView = device->CreateRenderTargetView(targets.colorTexture, colorViewDesc);
return targets.colorView != nullptr;
}
inline bool CreateViewportDepthResources(
RHI::RHIDevice* device,
ViewportRenderTargets& targets) {
const RHI::TextureDesc depthDesc =
BuildViewportTextureDesc(targets.width, targets.height, RHI::Format::D24_UNorm_S8_UInt);
targets.depthTexture = device->CreateTexture(depthDesc);
if (targets.depthTexture == nullptr) {
return false;
}
const RHI::ResourceViewDesc depthViewDesc =
BuildViewportTextureViewDesc(RHI::Format::D24_UNorm_S8_UInt);
targets.depthView = device->CreateDepthStencilView(targets.depthTexture, depthViewDesc);
return targets.depthView != nullptr;
}
inline bool CreateViewportObjectIdResources(
RHI::RHIDevice* device,
ViewportRenderTargets& targets) {
const RHI::TextureDesc objectIdDesc =
BuildViewportTextureDesc(targets.width, targets.height, RHI::Format::R8G8B8A8_UNorm);
targets.objectIdTexture = device->CreateTexture(objectIdDesc);
if (targets.objectIdTexture == nullptr) {
return false;
}
const RHI::ResourceViewDesc objectIdViewDesc =
BuildViewportTextureViewDesc(RHI::Format::R8G8B8A8_UNorm);
targets.objectIdView = device->CreateRenderTargetView(
targets.objectIdTexture,
objectIdViewDesc);
if (targets.objectIdView == nullptr) {
return false;
}
targets.objectIdShaderView = device->CreateShaderResourceView(
targets.objectIdTexture,
objectIdViewDesc);
return targets.objectIdShaderView != nullptr;
}
inline bool CreateViewportTextureDescriptor(
UI::ImGuiBackendBridge* backend,
RHI::RHIDevice* device,
ViewportRenderTargets& targets) {
return backend->CreateTextureDescriptor(
device,
targets.colorTexture,
&targets.imguiCpuHandle,
&targets.imguiGpuHandle,
&targets.textureId);
}
} // namespace Detail
inline void DestroyViewportRenderTargets(
UI::ImGuiBackendBridge* backend,
ViewportRenderTargets& targets) {
if (backend != nullptr && targets.imguiCpuHandle.ptr != 0) {
backend->FreeTextureDescriptor(targets.imguiCpuHandle, targets.imguiGpuHandle);
}
Detail::ShutdownAndDelete(targets.objectIdView);
Detail::ShutdownAndDelete(targets.objectIdShaderView);
Detail::ShutdownAndDelete(targets.objectIdTexture);
Detail::ShutdownAndDelete(targets.depthView);
Detail::ShutdownAndDelete(targets.depthTexture);
Detail::ShutdownAndDelete(targets.colorView);
Detail::ShutdownAndDelete(targets.colorTexture);
targets.width = 0;
targets.height = 0;
targets.imguiCpuHandle = {};
targets.imguiGpuHandle = {};
targets.textureId = {};
targets.colorState = RHI::ResourceStates::Common;
targets.objectIdState = RHI::ResourceStates::Common;
targets.hasValidObjectIdFrame = false;
}
inline bool CreateViewportRenderTargets(
EditorViewportKind kind,
uint32_t width,
uint32_t height,
RHI::RHIDevice* device,
UI::ImGuiBackendBridge* backend,
ViewportRenderTargets& targets) {
if (width == 0 || height == 0 || device == nullptr || backend == nullptr) {
return false;
}
DestroyViewportRenderTargets(backend, targets);
targets.width = width;
targets.height = height;
if (!Detail::CreateViewportColorResources(device, targets) ||
!Detail::CreateViewportDepthResources(device, targets) ||
(ViewportRequiresObjectIdResources(kind) &&
!Detail::CreateViewportObjectIdResources(device, targets)) ||
!Detail::CreateViewportTextureDescriptor(backend, device, targets)) {
DestroyViewportRenderTargets(backend, targets);
return false;
}
targets.colorState = RHI::ResourceStates::Common;
targets.objectIdState = RHI::ResourceStates::Common;
targets.hasValidObjectIdFrame = false;
return true;
}
} // namespace Editor
} // namespace XCEngine

View File

@@ -6,8 +6,8 @@
#include "IViewportHostService.h"
#include "SceneViewportPicker.h"
#include "SceneViewportCameraController.h"
#include "ViewportHostRenderTargets.h"
#include "ViewportObjectIdPicker.h"
#include "ViewportHostSurfaceUtils.h"
#include "UI/ImGuiBackendBridge.h"
#include <XCEngine/Components/CameraComponent.h>
@@ -122,7 +122,7 @@ public:
void Shutdown() {
for (ViewportEntry& entry : m_entries) {
DestroyViewportResources(entry);
DestroyViewportRenderTargets(m_backend, entry.renderTargets);
entry = {};
}
@@ -158,10 +158,12 @@ public:
}
EditorViewportFrame frame = {};
frame.textureId = entry.textureId;
frame.textureId = entry.renderTargets.textureId;
frame.requestedSize = requestedSize;
frame.renderSize = ImVec2(static_cast<float>(entry.width), static_cast<float>(entry.height));
frame.hasTexture = entry.textureId != ImTextureID{};
frame.renderSize = ImVec2(
static_cast<float>(entry.renderTargets.width),
static_cast<float>(entry.renderTargets.height));
frame.hasTexture = entry.renderTargets.textureId != ImTextureID{};
frame.wasRequested = entry.requestedThisFrame;
frame.statusText = entry.statusText;
return frame;
@@ -302,24 +304,10 @@ public:
private:
struct ViewportEntry {
EditorViewportKind kind = EditorViewportKind::Scene;
uint32_t width = 0;
uint32_t height = 0;
uint32_t requestedWidth = 0;
uint32_t requestedHeight = 0;
bool requestedThisFrame = false;
RHI::RHITexture* colorTexture = nullptr;
RHI::RHIResourceView* colorView = nullptr;
RHI::RHITexture* depthTexture = nullptr;
RHI::RHIResourceView* depthView = nullptr;
RHI::RHITexture* objectIdTexture = nullptr;
RHI::RHIResourceView* objectIdView = nullptr;
RHI::RHIResourceView* objectIdShaderView = nullptr;
D3D12_CPU_DESCRIPTOR_HANDLE imguiCpuHandle = {};
D3D12_GPU_DESCRIPTOR_HANDLE imguiGpuHandle = {};
ImTextureID textureId = {};
RHI::ResourceStates colorState = RHI::ResourceStates::Common;
RHI::ResourceStates objectIdState = RHI::ResourceStates::Common;
bool hasValidObjectIdFrame = false;
ViewportRenderTargets renderTargets = {};
std::string statusText;
};
@@ -413,81 +401,13 @@ private:
m_sceneViewCamera.controller.Focus(center);
}
bool CreateViewportColorResources(ViewportEntry& entry) {
const RHI::TextureDesc colorDesc =
BuildViewportTextureDesc(entry.width, entry.height, RHI::Format::R8G8B8A8_UNorm);
entry.colorTexture = m_device->CreateTexture(colorDesc);
if (entry.colorTexture == nullptr) {
return false;
}
const RHI::ResourceViewDesc colorViewDesc =
BuildViewportTextureViewDesc(RHI::Format::R8G8B8A8_UNorm);
entry.colorView = m_device->CreateRenderTargetView(entry.colorTexture, colorViewDesc);
return entry.colorView != nullptr;
}
bool CreateViewportDepthResources(ViewportEntry& entry) {
const RHI::TextureDesc depthDesc =
BuildViewportTextureDesc(entry.width, entry.height, RHI::Format::D24_UNorm_S8_UInt);
entry.depthTexture = m_device->CreateTexture(depthDesc);
if (entry.depthTexture == nullptr) {
return false;
}
const RHI::ResourceViewDesc depthViewDesc =
BuildViewportTextureViewDesc(RHI::Format::D24_UNorm_S8_UInt);
entry.depthView = m_device->CreateDepthStencilView(entry.depthTexture, depthViewDesc);
return entry.depthView != nullptr;
}
bool CreateSceneViewportObjectIdResources(ViewportEntry& entry) {
const RHI::TextureDesc objectIdDesc =
BuildViewportTextureDesc(entry.width, entry.height, RHI::Format::R8G8B8A8_UNorm);
entry.objectIdTexture = m_device->CreateTexture(objectIdDesc);
if (entry.objectIdTexture == nullptr) {
return false;
}
const RHI::ResourceViewDesc objectIdViewDesc =
BuildViewportTextureViewDesc(RHI::Format::R8G8B8A8_UNorm);
entry.objectIdView = m_device->CreateRenderTargetView(
entry.objectIdTexture,
objectIdViewDesc);
if (entry.objectIdView == nullptr) {
return false;
}
entry.objectIdShaderView = m_device->CreateShaderResourceView(
entry.objectIdTexture,
objectIdViewDesc);
return entry.objectIdShaderView != nullptr;
}
bool CreateViewportTextureDescriptor(ViewportEntry& entry) {
return m_backend->CreateTextureDescriptor(
m_device,
entry.colorTexture,
&entry.imguiCpuHandle,
&entry.imguiGpuHandle,
&entry.textureId);
}
bool EnsureViewportResources(ViewportEntry& entry) {
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{};
const ViewportHostResourceReuseQuery reuseQuery =
BuildViewportRenderTargetsReuseQuery(
entry.kind,
entry.renderTargets,
entry.requestedWidth,
entry.requestedHeight);
if (CanReuseViewportResources(reuseQuery)) {
return true;
}
@@ -496,53 +416,21 @@ private:
return false;
}
DestroyViewportResources(entry);
entry.width = entry.requestedWidth;
entry.height = entry.requestedHeight;
if (!CreateViewportColorResources(entry)) {
DestroyViewportResources(entry);
return false;
}
if (!CreateViewportDepthResources(entry)) {
DestroyViewportResources(entry);
return false;
}
if (ViewportRequiresObjectIdResources(entry.kind) &&
!CreateSceneViewportObjectIdResources(entry)) {
DestroyViewportResources(entry);
return false;
}
if (!CreateViewportTextureDescriptor(entry)) {
DestroyViewportResources(entry);
return false;
}
entry.colorState = RHI::ResourceStates::Common;
entry.objectIdState = RHI::ResourceStates::Common;
entry.hasValidObjectIdFrame = false;
return true;
return CreateViewportRenderTargets(
entry.kind,
entry.requestedWidth,
entry.requestedHeight,
m_device,
m_backend,
entry.renderTargets);
}
Rendering::RenderSurface BuildSurface(const ViewportEntry& entry) const {
return BuildViewportRenderSurface(
entry.width,
entry.height,
entry.colorView,
entry.depthView,
entry.colorState);
return BuildViewportColorSurface(entry.renderTargets);
}
Rendering::RenderSurface BuildObjectIdSurface(const ViewportEntry& entry) const {
return BuildViewportRenderSurface(
entry.width,
entry.height,
entry.objectIdView,
entry.depthView,
entry.objectIdState);
return BuildViewportObjectIdSurface(entry.renderTargets);
}
void AddSceneColorToRenderTargetPass(
@@ -552,10 +440,10 @@ private:
"SceneColorToRenderTarget",
[&entry](const Rendering::RenderPassContext& context) {
context.renderContext.commandList->TransitionBarrier(
entry.colorView,
entry.renderTargets.colorView,
context.surface.GetColorStateAfter(),
RHI::ResourceStates::RenderTarget);
entry.colorState = RHI::ResourceStates::RenderTarget;
entry.renderTargets.colorState = RHI::ResourceStates::RenderTarget;
return true;
}));
}
@@ -588,7 +476,7 @@ private:
const bool rendered = m_sceneSelectionOutlinePass.Render(
context.renderContext,
context.surface,
entry.objectIdShaderView,
entry.renderTargets.objectIdShaderView,
selectedObjectIds,
Rendering::Passes::ObjectIdOutlineStyle{
Math::Color(1.0f, 0.4f, 0.0f, 1.0f),
@@ -609,10 +497,10 @@ private:
"SceneColorToShaderResource",
[&entry](const Rendering::RenderPassContext& context) {
context.renderContext.commandList->TransitionBarrier(
entry.colorView,
entry.renderTargets.colorView,
RHI::ResourceStates::RenderTarget,
context.surface.GetColorStateAfter());
entry.colorState = context.surface.GetColorStateAfter();
entry.renderTargets.colorState = context.surface.GetColorStateAfter();
return true;
}));
}
@@ -626,14 +514,17 @@ private:
[this, &entry, selectedObjectIds](
const Rendering::RenderPassContext& context) {
const float debugClearColor[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
RHI::RHIResourceView* colorView = entry.colorView;
context.renderContext.commandList->SetRenderTargets(1, &colorView, entry.depthView);
RHI::RHIResourceView* colorView = entry.renderTargets.colorView;
context.renderContext.commandList->SetRenderTargets(
1,
&colorView,
entry.renderTargets.depthView);
context.renderContext.commandList->ClearRenderTarget(colorView, debugClearColor);
const bool rendered = m_sceneSelectionOutlinePass.Render(
context.renderContext,
context.surface,
entry.objectIdShaderView,
entry.renderTargets.objectIdShaderView,
selectedObjectIds,
Rendering::Passes::ObjectIdOutlineStyle{
Math::Color(1.0f, 0.4f, 0.0f, 1.0f),
@@ -683,7 +574,7 @@ private:
const std::vector<uint64_t>& selectedObjectIds,
Rendering::RenderPassSequence& outPostPasses) {
const bool hasSelection = !selectedObjectIds.empty();
const bool hasObjectIdShaderView = entry.objectIdShaderView != nullptr;
const bool hasObjectIdShaderView = entry.renderTargets.objectIdShaderView != nullptr;
if (hasSelection &&
!kDebugSceneSelectionMask &&
@@ -744,7 +635,7 @@ private:
if (!EnsureSceneViewCamera()) {
entry.statusText = "Scene view camera is unavailable";
ClearViewport(entry, renderContext, 0.18f, 0.07f, 0.07f, 1.0f);
entry.hasValidObjectIdFrame = false;
entry.renderTargets.hasValidObjectIdFrame = false;
return false;
}
@@ -753,7 +644,7 @@ private:
if (scene == nullptr) {
entry.statusText = "No active scene";
ClearViewport(entry, renderContext, 0.07f, 0.08f, 0.10f, 1.0f);
entry.hasValidObjectIdFrame = false;
entry.renderTargets.hasValidObjectIdFrame = false;
return false;
}
@@ -765,7 +656,7 @@ private:
if (requests.empty()) {
SetViewportStatusIfEmpty(entry.statusText, "Scene renderer failed");
ClearViewport(entry, renderContext, 0.18f, 0.07f, 0.07f, 1.0f);
entry.hasValidObjectIdFrame = false;
entry.renderTargets.hasValidObjectIdFrame = false;
return false;
}
@@ -773,7 +664,7 @@ private:
requests[0].postScenePasses = &sceneState.postPasses;
}
if (entry.objectIdView != nullptr) {
if (entry.renderTargets.objectIdView != nullptr) {
requests[0].objectId.surface = BuildObjectIdSurface(entry);
requests[0].objectId.surface.SetRenderArea(requests[0].surface.GetRenderArea());
}
@@ -781,13 +672,13 @@ private:
if (!m_sceneRenderer->Render(requests)) {
SetViewportStatusIfEmpty(entry.statusText, "Scene renderer failed");
ClearViewport(entry, renderContext, 0.18f, 0.07f, 0.07f, 1.0f);
entry.hasValidObjectIdFrame = false;
entry.renderTargets.hasValidObjectIdFrame = false;
return false;
}
entry.colorState = RHI::ResourceStates::PixelShaderResource;
entry.objectIdState = RHI::ResourceStates::PixelShaderResource;
entry.hasValidObjectIdFrame = requests[0].objectId.IsRequested();
entry.renderTargets.colorState = RHI::ResourceStates::PixelShaderResource;
entry.renderTargets.objectIdState = RHI::ResourceStates::PixelShaderResource;
entry.renderTargets.hasValidObjectIdFrame = requests[0].objectId.IsRequested();
return true;
}
@@ -812,11 +703,11 @@ private:
if (!m_sceneRenderer->Render(*scene, nullptr, renderContext, surface)) {
entry.statusText = "Scene renderer failed";
ClearViewport(entry, renderContext, 0.18f, 0.07f, 0.07f, 1.0f);
entry.hasValidObjectIdFrame = false;
entry.renderTargets.hasValidObjectIdFrame = false;
return false;
}
entry.colorState = RHI::ResourceStates::PixelShaderResource;
entry.renderTargets.colorState = RHI::ResourceStates::PixelShaderResource;
entry.statusText.clear();
return true;
}
@@ -826,7 +717,7 @@ private:
IEditorContext& context,
const Components::Scene* scene,
const Rendering::RenderContext& renderContext) {
if (entry.colorView == nullptr || entry.depthView == nullptr) {
if (entry.renderTargets.colorView == nullptr || entry.renderTargets.depthView == nullptr) {
entry.statusText = "Viewport render target is unavailable";
return;
}
@@ -854,21 +745,22 @@ private:
return;
}
ViewportRenderTargets& targets = entry.renderTargets;
const float clearColor[4] = { r, g, b, a };
RHI::RHIResourceView* colorView = entry.colorView;
RHI::RHIResourceView* colorView = targets.colorView;
commandList->TransitionBarrier(
colorView,
entry.colorState,
targets.colorState,
RHI::ResourceStates::RenderTarget);
commandList->SetRenderTargets(1, &colorView, entry.depthView);
commandList->SetRenderTargets(1, &colorView, targets.depthView);
commandList->ClearRenderTarget(colorView, clearColor);
commandList->ClearDepthStencil(entry.depthView, 1.0f, 0);
commandList->ClearDepthStencil(targets.depthView, 1.0f, 0);
commandList->TransitionBarrier(
colorView,
RHI::ResourceStates::RenderTarget,
RHI::ResourceStates::PixelShaderResource);
entry.colorState = RHI::ResourceStates::PixelShaderResource;
entry.hasValidObjectIdFrame = false;
targets.colorState = RHI::ResourceStates::PixelShaderResource;
targets.hasValidObjectIdFrame = false;
}
bool TryPickSceneViewEntityWithObjectId(
@@ -883,11 +775,11 @@ private:
ViewportObjectIdPickContext pickContext = {};
pickContext.commandQueue = m_sceneViewLastRenderContext.commandQueue;
pickContext.texture = entry.objectIdTexture;
pickContext.textureState = entry.objectIdState;
pickContext.textureWidth = entry.width;
pickContext.textureHeight = entry.height;
pickContext.hasValidFrame = entry.hasValidObjectIdFrame;
pickContext.texture = entry.renderTargets.objectIdTexture;
pickContext.textureState = entry.renderTargets.objectIdState;
pickContext.textureWidth = entry.renderTargets.width;
pickContext.textureHeight = entry.renderTargets.height;
pickContext.hasValidFrame = entry.renderTargets.hasValidObjectIdFrame;
pickContext.viewportSize = viewportSize;
pickContext.viewportMousePosition = viewportMousePosition;
@@ -906,63 +798,6 @@ private:
outEntityId);
}
void DestroyViewportResources(ViewportEntry& entry) {
if (m_backend != nullptr && entry.imguiCpuHandle.ptr != 0) {
m_backend->FreeTextureDescriptor(entry.imguiCpuHandle, entry.imguiGpuHandle);
}
if (entry.objectIdView != nullptr) {
entry.objectIdView->Shutdown();
delete entry.objectIdView;
entry.objectIdView = nullptr;
}
if (entry.objectIdShaderView != nullptr) {
entry.objectIdShaderView->Shutdown();
delete entry.objectIdShaderView;
entry.objectIdShaderView = nullptr;
}
if (entry.objectIdTexture != nullptr) {
entry.objectIdTexture->Shutdown();
delete entry.objectIdTexture;
entry.objectIdTexture = nullptr;
}
if (entry.depthView != nullptr) {
entry.depthView->Shutdown();
delete entry.depthView;
entry.depthView = nullptr;
}
if (entry.depthTexture != nullptr) {
entry.depthTexture->Shutdown();
delete entry.depthTexture;
entry.depthTexture = nullptr;
}
if (entry.colorView != nullptr) {
entry.colorView->Shutdown();
delete entry.colorView;
entry.colorView = nullptr;
}
if (entry.colorTexture != nullptr) {
entry.colorTexture->Shutdown();
delete entry.colorTexture;
entry.colorTexture = nullptr;
}
entry.width = 0;
entry.height = 0;
entry.imguiCpuHandle = {};
entry.imguiGpuHandle = {};
entry.textureId = {};
entry.colorState = RHI::ResourceStates::Common;
entry.objectIdState = RHI::ResourceStates::Common;
entry.hasValidObjectIdFrame = false;
}
UI::ImGuiBackendBridge* m_backend = nullptr;
RHI::RHIDevice* m_device = nullptr;
std::unique_ptr<Rendering::SceneRenderer> m_sceneRenderer;

View File

@@ -12,6 +12,7 @@ set(EDITOR_TEST_SOURCES
test_scene_viewport_overlay_renderer.cpp
test_viewport_host_surface_utils.cpp
test_viewport_object_id_picker.cpp
test_viewport_render_targets.cpp
test_builtin_icon_layout_utils.cpp
${CMAKE_SOURCE_DIR}/editor/src/Core/UndoManager.cpp
${CMAKE_SOURCE_DIR}/editor/src/Managers/SceneManager.cpp

View File

@@ -0,0 +1,180 @@
#include <gtest/gtest.h>
#include "Viewport/ViewportHostRenderTargets.h"
#include <string>
namespace {
using XCEngine::Editor::BuildViewportColorSurface;
using XCEngine::Editor::BuildViewportObjectIdSurface;
using XCEngine::Editor::BuildViewportRenderTargetsReuseQuery;
using XCEngine::Editor::DestroyViewportRenderTargets;
using XCEngine::Editor::EditorViewportKind;
using XCEngine::Editor::ViewportRenderTargets;
using XCEngine::RHI::Format;
using XCEngine::RHI::RHIResourceView;
using XCEngine::RHI::RHITexture;
using XCEngine::RHI::ResourceStates;
using XCEngine::RHI::ResourceViewDimension;
using XCEngine::RHI::ResourceViewType;
using XCEngine::RHI::TextureType;
class DummyTexture final : public RHITexture {
public:
explicit DummyTexture(Format format = Format::R8G8B8A8_UNorm)
: m_format(format) {
}
uint32_t GetWidth() const override { return 1; }
uint32_t GetHeight() const override { return 1; }
uint32_t GetDepth() const override { return 1; }
uint32_t GetMipLevels() const override { return 1; }
Format GetFormat() const override { return m_format; }
TextureType GetTextureType() const override { return TextureType::Texture2D; }
ResourceStates GetState() const override { return m_state; }
void SetState(ResourceStates state) override { m_state = state; }
void* GetNativeHandle() override { return nullptr; }
const std::string& GetName() const override { return m_name; }
void SetName(const std::string& name) override { m_name = name; }
void Shutdown() override { shutdownCalled = true; }
bool shutdownCalled = false;
private:
Format m_format = Format::R8G8B8A8_UNorm;
ResourceStates m_state = ResourceStates::Common;
std::string m_name;
};
class DummyResourceView final : public RHIResourceView {
public:
explicit DummyResourceView(
ResourceViewType viewType = ResourceViewType::RenderTarget,
Format format = Format::R8G8B8A8_UNorm)
: m_viewType(viewType)
, m_format(format) {
}
void Shutdown() override { shutdownCalled = true; }
void* GetNativeHandle() override { return nullptr; }
bool IsValid() const override { return true; }
ResourceViewType GetViewType() const override { return m_viewType; }
ResourceViewDimension GetDimension() const override { return ResourceViewDimension::Texture2D; }
Format GetFormat() const override { return m_format; }
bool shutdownCalled = false;
private:
ResourceViewType m_viewType = ResourceViewType::RenderTarget;
Format m_format = Format::R8G8B8A8_UNorm;
};
TEST(ViewportRenderTargetsTest, BuildReuseQueryReflectsCurrentResourcePresence) {
ViewportRenderTargets targets = {};
targets.width = 1280;
targets.height = 720;
targets.colorTexture = reinterpret_cast<RHITexture*>(static_cast<uintptr_t>(0x1));
targets.colorView = reinterpret_cast<RHIResourceView*>(static_cast<uintptr_t>(0x2));
targets.depthTexture = reinterpret_cast<RHITexture*>(static_cast<uintptr_t>(0x3));
targets.depthView = reinterpret_cast<RHIResourceView*>(static_cast<uintptr_t>(0x4));
targets.objectIdTexture = reinterpret_cast<RHITexture*>(static_cast<uintptr_t>(0x5));
targets.objectIdView = reinterpret_cast<RHIResourceView*>(static_cast<uintptr_t>(0x6));
targets.objectIdShaderView = reinterpret_cast<RHIResourceView*>(static_cast<uintptr_t>(0x7));
targets.textureId = static_cast<ImTextureID>(static_cast<uintptr_t>(0x8));
const auto query =
BuildViewportRenderTargetsReuseQuery(EditorViewportKind::Scene, targets, 1280, 720);
EXPECT_EQ(query.width, 1280u);
EXPECT_EQ(query.height, 720u);
EXPECT_TRUE(query.resources.hasColorTexture);
EXPECT_TRUE(query.resources.hasColorView);
EXPECT_TRUE(query.resources.hasDepthTexture);
EXPECT_TRUE(query.resources.hasDepthView);
EXPECT_TRUE(query.resources.hasObjectIdTexture);
EXPECT_TRUE(query.resources.hasObjectIdView);
EXPECT_TRUE(query.resources.hasObjectIdShaderView);
EXPECT_TRUE(query.resources.hasTextureDescriptor);
}
TEST(ViewportRenderTargetsTest, BuildSurfaceUsesTargetAttachmentsAndStates) {
DummyResourceView colorView(ResourceViewType::RenderTarget);
DummyResourceView depthView(ResourceViewType::DepthStencil, Format::D24_UNorm_S8_UInt);
DummyResourceView objectIdView(ResourceViewType::RenderTarget);
ViewportRenderTargets targets = {};
targets.width = 800;
targets.height = 600;
targets.colorView = &colorView;
targets.depthView = &depthView;
targets.objectIdView = &objectIdView;
targets.colorState = ResourceStates::Common;
targets.objectIdState = ResourceStates::PixelShaderResource;
const auto colorSurface = BuildViewportColorSurface(targets);
ASSERT_EQ(colorSurface.GetColorAttachments().size(), 1u);
EXPECT_EQ(colorSurface.GetColorAttachments()[0], &colorView);
EXPECT_EQ(colorSurface.GetDepthAttachment(), &depthView);
EXPECT_EQ(colorSurface.GetColorStateBefore(), ResourceStates::Common);
const auto objectIdSurface = BuildViewportObjectIdSurface(targets);
ASSERT_EQ(objectIdSurface.GetColorAttachments().size(), 1u);
EXPECT_EQ(objectIdSurface.GetColorAttachments()[0], &objectIdView);
EXPECT_EQ(objectIdSurface.GetDepthAttachment(), &depthView);
EXPECT_EQ(objectIdSurface.GetColorStateBefore(), ResourceStates::PixelShaderResource);
}
TEST(ViewportRenderTargetsTest, DestroyViewportRenderTargetsShutsDownAndClearsState) {
auto* colorTexture = new DummyTexture();
auto* colorView = new DummyResourceView(ResourceViewType::RenderTarget);
auto* depthTexture = new DummyTexture(Format::D24_UNorm_S8_UInt);
auto* depthView = new DummyResourceView(ResourceViewType::DepthStencil, Format::D24_UNorm_S8_UInt);
auto* objectIdTexture = new DummyTexture();
auto* objectIdView = new DummyResourceView(ResourceViewType::RenderTarget);
auto* objectIdShaderView = new DummyResourceView(ResourceViewType::ShaderResource);
ViewportRenderTargets targets = {};
targets.width = 640;
targets.height = 360;
targets.colorTexture = colorTexture;
targets.colorView = colorView;
targets.depthTexture = depthTexture;
targets.depthView = depthView;
targets.objectIdTexture = objectIdTexture;
targets.objectIdView = objectIdView;
targets.objectIdShaderView = objectIdShaderView;
targets.imguiCpuHandle.ptr = 123;
targets.imguiGpuHandle.ptr = 456;
targets.textureId = static_cast<ImTextureID>(static_cast<uintptr_t>(789));
targets.colorState = ResourceStates::RenderTarget;
targets.objectIdState = ResourceStates::PixelShaderResource;
targets.hasValidObjectIdFrame = true;
DestroyViewportRenderTargets(nullptr, targets);
EXPECT_TRUE(colorTexture->shutdownCalled);
EXPECT_TRUE(colorView->shutdownCalled);
EXPECT_TRUE(depthTexture->shutdownCalled);
EXPECT_TRUE(depthView->shutdownCalled);
EXPECT_TRUE(objectIdTexture->shutdownCalled);
EXPECT_TRUE(objectIdView->shutdownCalled);
EXPECT_TRUE(objectIdShaderView->shutdownCalled);
EXPECT_EQ(targets.width, 0u);
EXPECT_EQ(targets.height, 0u);
EXPECT_EQ(targets.colorTexture, nullptr);
EXPECT_EQ(targets.colorView, nullptr);
EXPECT_EQ(targets.depthTexture, nullptr);
EXPECT_EQ(targets.depthView, nullptr);
EXPECT_EQ(targets.objectIdTexture, nullptr);
EXPECT_EQ(targets.objectIdView, nullptr);
EXPECT_EQ(targets.objectIdShaderView, nullptr);
EXPECT_EQ(targets.imguiCpuHandle.ptr, 0u);
EXPECT_EQ(targets.imguiGpuHandle.ptr, 0u);
EXPECT_EQ(targets.textureId, ImTextureID{});
EXPECT_EQ(targets.colorState, ResourceStates::Common);
EXPECT_EQ(targets.objectIdState, ResourceStates::Common);
EXPECT_FALSE(targets.hasValidObjectIdFrame);
}
} // namespace