diff --git a/editor/src/Viewport/ViewportHostRenderTargets.h b/editor/src/Viewport/ViewportHostRenderTargets.h new file mode 100644 index 00000000..27b31817 --- /dev/null +++ b/editor/src/Viewport/ViewportHostRenderTargets.h @@ -0,0 +1,211 @@ +#pragma once + +#include "ViewportHostSurfaceUtils.h" +#include "UI/ImGuiBackendBridge.h" + +#include +#include +#include + +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 +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 diff --git a/editor/src/Viewport/ViewportHostService.h b/editor/src/Viewport/ViewportHostService.h index 503e86d1..3bbb7cd9 100644 --- a/editor/src/Viewport/ViewportHostService.h +++ b/editor/src/Viewport/ViewportHostService.h @@ -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 @@ -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(entry.width), static_cast(entry.height)); - frame.hasTexture = entry.textureId != ImTextureID{}; + frame.renderSize = ImVec2( + static_cast(entry.renderTargets.width), + static_cast(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& 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 m_sceneRenderer; diff --git a/tests/editor/CMakeLists.txt b/tests/editor/CMakeLists.txt index 7496e91b..ffa42d3f 100644 --- a/tests/editor/CMakeLists.txt +++ b/tests/editor/CMakeLists.txt @@ -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 diff --git a/tests/editor/test_viewport_render_targets.cpp b/tests/editor/test_viewport_render_targets.cpp new file mode 100644 index 00000000..fc607718 --- /dev/null +++ b/tests/editor/test_viewport_render_targets.cpp @@ -0,0 +1,180 @@ +#include + +#include "Viewport/ViewportHostRenderTargets.h" + +#include + +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(static_cast(0x1)); + targets.colorView = reinterpret_cast(static_cast(0x2)); + targets.depthTexture = reinterpret_cast(static_cast(0x3)); + targets.depthView = reinterpret_cast(static_cast(0x4)); + targets.objectIdTexture = reinterpret_cast(static_cast(0x5)); + targets.objectIdView = reinterpret_cast(static_cast(0x6)); + targets.objectIdShaderView = reinterpret_cast(static_cast(0x7)); + targets.textureId = static_cast(static_cast(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(static_cast(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