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

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