关键节点
This commit is contained in:
325
editor/app/Rendering/Viewport/ViewportHostService.cpp
Normal file
325
editor/app/Rendering/Viewport/ViewportHostService.cpp
Normal file
@@ -0,0 +1,325 @@
|
||||
#include "ViewportHostService.h"
|
||||
|
||||
#include "Rendering/Host/ViewportRenderHost.h"
|
||||
|
||||
#include <XCEngine/RHI/RHICommandList.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace XCEngine::UI::Editor::App {
|
||||
|
||||
namespace {
|
||||
|
||||
using ::XCEngine::RHI::ResourceStates;
|
||||
|
||||
} // namespace
|
||||
|
||||
ViewportHostService::ViewportHostService() = default;
|
||||
|
||||
ViewportHostService::~ViewportHostService() = default;
|
||||
|
||||
void ViewportHostService::AttachWindowRenderer(
|
||||
Rendering::Host::ViewportRenderHost& windowRenderer) {
|
||||
if (m_windowRenderer == &windowRenderer) {
|
||||
m_device = windowRenderer.GetRHIDevice();
|
||||
m_retiredTargetsBySlot.resize(windowRenderer.GetViewportResourceRetirementSlotCount());
|
||||
return;
|
||||
}
|
||||
|
||||
ReleaseWindowResources();
|
||||
m_windowRenderer = &windowRenderer;
|
||||
m_device = windowRenderer.GetRHIDevice();
|
||||
m_retiredTargetsBySlot.resize(windowRenderer.GetViewportResourceRetirementSlotCount());
|
||||
}
|
||||
|
||||
void ViewportHostService::DetachWindowRenderer() {
|
||||
ReleaseWindowResources();
|
||||
}
|
||||
|
||||
void ViewportHostService::SetSurfacePresentationEnabled(bool enabled) {
|
||||
m_surfacePresentationEnabled = enabled;
|
||||
}
|
||||
|
||||
void ViewportHostService::SetContentRenderer(
|
||||
std::string_view viewportId,
|
||||
IViewportContentRenderer* renderer,
|
||||
const ViewportResourceRequirements& requirements) {
|
||||
ViewportEntry& entry = GetOrCreateEntry(viewportId);
|
||||
entry.renderer = renderer;
|
||||
entry.requirements = requirements;
|
||||
}
|
||||
|
||||
void ViewportHostService::Shutdown() {
|
||||
for (auto& [viewportId, entry] : m_entries) {
|
||||
DestroyViewportEntry(entry);
|
||||
}
|
||||
DestroyRetiredTargets();
|
||||
|
||||
m_windowRenderer = nullptr;
|
||||
m_device = nullptr;
|
||||
m_surfacePresentationEnabled = false;
|
||||
m_entries.clear();
|
||||
m_retiredTargetsBySlot.clear();
|
||||
}
|
||||
|
||||
void ViewportHostService::BeginFrame() {
|
||||
ReclaimRetiredTargetsForCurrentSlot();
|
||||
for (auto& [viewportId, entry] : m_entries) {
|
||||
entry.requestedWidth = 0;
|
||||
entry.requestedHeight = 0;
|
||||
entry.requestedThisFrame = false;
|
||||
entry.renderedThisFrame = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ViewportHostService::ReleaseWindowResources() {
|
||||
for (auto& [viewportId, entry] : m_entries) {
|
||||
DestroyViewportEntry(entry);
|
||||
}
|
||||
DestroyRetiredTargets();
|
||||
|
||||
m_windowRenderer = nullptr;
|
||||
m_device = nullptr;
|
||||
m_surfacePresentationEnabled = false;
|
||||
m_retiredTargetsBySlot.clear();
|
||||
}
|
||||
|
||||
ViewportHostService::ViewportEntry& ViewportHostService::GetOrCreateEntry(
|
||||
std::string_view viewportId) {
|
||||
const auto [it, inserted] =
|
||||
m_entries.try_emplace(std::string(viewportId));
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void ViewportHostService::DestroyViewportEntry(ViewportEntry& entry) {
|
||||
m_renderTargetManager.DestroyTargets(
|
||||
m_windowRenderer,
|
||||
entry.renderTargets);
|
||||
entry.requestedWidth = 0;
|
||||
entry.requestedHeight = 0;
|
||||
entry.requestedThisFrame = false;
|
||||
entry.renderedThisFrame = false;
|
||||
entry.renderTargets = {};
|
||||
entry.statusText.clear();
|
||||
}
|
||||
|
||||
void ViewportHostService::DestroyRetiredTargets() {
|
||||
for (auto& retiredTargets : m_retiredTargetsBySlot) {
|
||||
for (ViewportRenderTargets& targets : retiredTargets) {
|
||||
m_renderTargetManager.DestroyTargets(m_windowRenderer, targets);
|
||||
}
|
||||
retiredTargets.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void ViewportHostService::ReclaimRetiredTargetsForCurrentSlot() {
|
||||
if (m_windowRenderer == nullptr || m_retiredTargetsBySlot.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::uint32_t activeSlot = 0u;
|
||||
if (!m_windowRenderer->TryGetActiveViewportResourceRetirementSlot(activeSlot) ||
|
||||
activeSlot >= m_retiredTargetsBySlot.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (ViewportRenderTargets& targets : m_retiredTargetsBySlot[activeSlot]) {
|
||||
m_renderTargetManager.DestroyTargets(m_windowRenderer, targets);
|
||||
}
|
||||
m_retiredTargetsBySlot[activeSlot].clear();
|
||||
}
|
||||
|
||||
void ViewportHostService::RetireViewportTargets(ViewportRenderTargets& targets) {
|
||||
if (targets.colorTexture == nullptr &&
|
||||
targets.colorView == nullptr &&
|
||||
targets.depthTexture == nullptr &&
|
||||
targets.depthView == nullptr &&
|
||||
!targets.textureHandle.IsValid()) {
|
||||
targets = {};
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_windowRenderer == nullptr || m_retiredTargetsBySlot.empty()) {
|
||||
m_renderTargetManager.DestroyTargets(nullptr, targets);
|
||||
return;
|
||||
}
|
||||
|
||||
std::uint32_t activeSlot = 0u;
|
||||
if (!m_windowRenderer->TryGetActiveViewportResourceRetirementSlot(activeSlot) ||
|
||||
activeSlot >= m_retiredTargetsBySlot.size()) {
|
||||
m_renderTargetManager.DestroyTargets(m_windowRenderer, targets);
|
||||
return;
|
||||
}
|
||||
|
||||
m_retiredTargetsBySlot[activeSlot].push_back({});
|
||||
std::swap(m_retiredTargetsBySlot[activeSlot].back(), targets);
|
||||
}
|
||||
|
||||
ViewportFrame ViewportHostService::RequestViewport(
|
||||
std::string_view viewportId,
|
||||
const ::XCEngine::UI::UISize& requestedSize) {
|
||||
ViewportEntry& entry = GetOrCreateEntry(viewportId);
|
||||
entry.requestedThisFrame = requestedSize.width > 1.0f && requestedSize.height > 1.0f;
|
||||
entry.requestedWidth = entry.requestedThisFrame
|
||||
? static_cast<std::uint32_t>(requestedSize.width)
|
||||
: 0u;
|
||||
entry.requestedHeight = entry.requestedThisFrame
|
||||
? static_cast<std::uint32_t>(requestedSize.height)
|
||||
: 0u;
|
||||
|
||||
if (!entry.requestedThisFrame) {
|
||||
return BuildFrame(entry, requestedSize);
|
||||
}
|
||||
|
||||
if (m_windowRenderer == nullptr || m_device == nullptr) {
|
||||
return BuildFrame(entry, requestedSize);
|
||||
}
|
||||
|
||||
if (!EnsureViewportResources(entry)) {
|
||||
return BuildFrame(entry, requestedSize);
|
||||
}
|
||||
|
||||
return BuildFrame(entry, requestedSize);
|
||||
}
|
||||
|
||||
void ViewportHostService::RenderRequestedViewports(
|
||||
const ::XCEngine::Rendering::RenderContext& renderContext) {
|
||||
if (m_windowRenderer == nullptr || m_device == nullptr || !renderContext.IsValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& [viewportId, entry] : m_entries) {
|
||||
if (!entry.requestedThisFrame || !EnsureViewportResources(entry)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
IViewportContentRenderer* renderer = entry.renderer;
|
||||
if (renderer != nullptr) {
|
||||
const ViewportRenderResult renderResult =
|
||||
renderer->Render(
|
||||
entry.renderTargets,
|
||||
*m_device,
|
||||
renderContext);
|
||||
entry.statusText = renderResult.statusText;
|
||||
if (!renderResult.rendered && renderResult.requiresFallbackClear) {
|
||||
ApplyViewportFallback(
|
||||
entry,
|
||||
renderContext,
|
||||
renderResult);
|
||||
}
|
||||
} else {
|
||||
entry.statusText.clear();
|
||||
ClearViewport(entry, renderContext, 0.09f, 0.09f, 0.09f, 1.0f);
|
||||
}
|
||||
|
||||
entry.renderedThisFrame = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool ViewportHostService::EnsureViewportResources(ViewportEntry& entry) {
|
||||
const ViewportResourceReuseQuery reuseQuery =
|
||||
BuildViewportRenderTargetsReuseQuery(
|
||||
entry.requirements,
|
||||
entry.renderTargets,
|
||||
entry.requestedWidth,
|
||||
entry.requestedHeight);
|
||||
if (CanReuseViewportResources(reuseQuery)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m_windowRenderer == nullptr ||
|
||||
m_device == nullptr ||
|
||||
entry.requestedWidth == 0u ||
|
||||
entry.requestedHeight == 0u) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ViewportRenderTargets nextTargets = {};
|
||||
if (!m_renderTargetManager.CreateTargets(
|
||||
entry.requirements,
|
||||
entry.requestedWidth,
|
||||
entry.requestedHeight,
|
||||
*m_device,
|
||||
*m_windowRenderer,
|
||||
nextTargets)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ViewportRenderTargets retiredTargets = {};
|
||||
std::swap(retiredTargets, entry.renderTargets);
|
||||
std::swap(entry.renderTargets, nextTargets);
|
||||
RetireViewportTargets(retiredTargets);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ViewportHostService::ApplyViewportFallback(
|
||||
ViewportEntry& entry,
|
||||
const ::XCEngine::Rendering::RenderContext& renderContext,
|
||||
const ViewportRenderResult& renderResult) {
|
||||
entry.statusText = renderResult.statusText;
|
||||
entry.renderTargets.hasValidObjectIdFrame = false;
|
||||
entry.renderTargets.objectIdFrameSerial = 0u;
|
||||
ClearViewport(
|
||||
entry,
|
||||
renderContext,
|
||||
renderResult.fallbackClearR,
|
||||
renderResult.fallbackClearG,
|
||||
renderResult.fallbackClearB,
|
||||
renderResult.fallbackClearA);
|
||||
}
|
||||
|
||||
void ViewportHostService::ClearViewport(
|
||||
ViewportEntry& entry,
|
||||
const ::XCEngine::Rendering::RenderContext& renderContext,
|
||||
float r,
|
||||
float g,
|
||||
float b,
|
||||
float a) {
|
||||
if (renderContext.commandList == nullptr ||
|
||||
entry.renderTargets.colorView == nullptr ||
|
||||
entry.renderTargets.depthView == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* commandList = renderContext.commandList;
|
||||
auto* colorView = entry.renderTargets.colorView;
|
||||
const float clearColor[4] = { r, g, b, a };
|
||||
commandList->TransitionBarrier(
|
||||
colorView,
|
||||
entry.renderTargets.colorState,
|
||||
ResourceStates::RenderTarget);
|
||||
commandList->SetRenderTargets(1, &colorView, entry.renderTargets.depthView);
|
||||
commandList->ClearRenderTarget(colorView, clearColor);
|
||||
commandList->ClearDepthStencil(entry.renderTargets.depthView, 1.0f, 0);
|
||||
commandList->TransitionBarrier(
|
||||
colorView,
|
||||
ResourceStates::RenderTarget,
|
||||
ResourceStates::PixelShaderResource);
|
||||
entry.renderTargets.colorState = ResourceStates::PixelShaderResource;
|
||||
entry.renderTargets.objectIdState = ResourceStates::Common;
|
||||
entry.renderTargets.selectionMaskState = ResourceStates::Common;
|
||||
entry.renderTargets.hasValidObjectIdFrame = false;
|
||||
entry.renderTargets.objectIdFrameSerial = 0u;
|
||||
}
|
||||
|
||||
ViewportFrame ViewportHostService::BuildFrame(
|
||||
const ViewportEntry& entry,
|
||||
const ::XCEngine::UI::UISize& requestedSize) const {
|
||||
ViewportFrame frame = {};
|
||||
frame.requestedSize = requestedSize;
|
||||
frame.renderSize = ::XCEngine::UI::UISize(
|
||||
static_cast<float>(entry.renderTargets.width),
|
||||
static_cast<float>(entry.renderTargets.height));
|
||||
frame.wasRequested = entry.requestedThisFrame;
|
||||
frame.statusText = entry.statusText;
|
||||
|
||||
if (m_surfacePresentationEnabled &&
|
||||
entry.renderTargets.textureHandle.IsValid()) {
|
||||
frame.texture = entry.renderTargets.textureHandle;
|
||||
frame.hasTexture = true;
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
} // namespace XCEngine::UI::Editor::App
|
||||
Reference in New Issue
Block a user