refactor: extract viewport render targets
This commit is contained in:
211
editor/src/Viewport/ViewportHostRenderTargets.h
Normal file
211
editor/src/Viewport/ViewportHostRenderTargets.h
Normal 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
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
180
tests/editor/test_viewport_render_targets.cpp
Normal file
180
tests/editor/test_viewport_render_targets.cpp
Normal 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
|
||||
Reference in New Issue
Block a user