Files
XCEngine/editor/app/Rendering/Viewport/SceneViewportRenderService.cpp

234 lines
7.1 KiB
C++
Raw Normal View History

2026-04-27 19:16:08 +08:00
#include "Viewport/SceneViewportRenderService.h"
#include "Engine/EditorShaderProvider.h"
#include "Engine/SceneViewportEngineBridge.h"
2026-04-28 02:57:49 +08:00
#include "Viewport/ViewportObjectIdPicker.h"
#include <utility>
namespace XCEngine::UI::Editor::App {
namespace {
ViewportRenderResult BuildFallbackResult(
std::string statusText,
float r,
float g,
float b,
float a) {
ViewportRenderResult result = {};
result.rendered = false;
result.requiresFallbackClear = true;
result.statusText = std::move(statusText);
result.fallbackClearR = r;
result.fallbackClearG = g;
result.fallbackClearB = b;
result.fallbackClearA = a;
return result;
}
void SetStatusIfEmpty(
std::string& statusText,
std::string_view message) {
if (statusText.empty()) {
statusText = std::string(message);
}
}
} // namespace
SceneViewportRenderService::SceneViewportRenderService() = default;
SceneViewportRenderService::~SceneViewportRenderService() = default;
2026-04-28 02:57:49 +08:00
EditorViewportResourceRequirements
SceneViewportRenderService::GetViewportResourceRequirements() {
2026-04-28 02:57:49 +08:00
EditorViewportResourceRequirements requirements = {};
requirements.requiresDepthSampling = true;
requirements.requiresObjectIdSurface = true;
requirements.requiresSelectionMaskSurface = true;
return requirements;
}
void SceneViewportRenderService::Initialize(
const SceneViewportShaderPaths& shaderPaths,
SceneViewportEngineBridge& engineBridge,
EditorShaderProvider& shaderProvider) {
m_engineBridge = &engineBridge;
m_renderPassBundle.Initialize(shaderPaths, shaderProvider);
}
void SceneViewportRenderService::Shutdown() {
m_renderRequest = {};
m_renderPassBundle.Shutdown();
m_engineBridge = nullptr;
m_device = nullptr;
m_lastTargets = nullptr;
m_lastRenderContext = {};
2026-04-29 01:24:21 +08:00
m_lastObjectIdRequestRevision = 0u;
m_objectIdFrameSerialCounter = 0u;
}
void SceneViewportRenderService::SetRenderRequest(
SceneViewportRenderRequest request) {
if (m_lastTargets != nullptr &&
m_lastTargets->hasValidObjectIdFrame &&
2026-04-29 01:24:21 +08:00
request.requestRevision != m_lastObjectIdRequestRevision) {
InvalidateObjectIdFrame();
}
m_renderRequest = std::move(request);
}
2026-04-28 02:57:49 +08:00
const IViewportObjectPickerService& SceneViewportRenderService::GetObjectPicker() const {
return *this;
}
ViewportRenderResult SceneViewportRenderService::Render(
ViewportRenderTargets& targets,
::XCEngine::RHI::RHIDevice& device,
const ::XCEngine::Rendering::RenderContext& renderContext) {
m_device = &device;
m_lastTargets = &targets;
m_lastRenderContext = renderContext;
2026-04-29 01:24:21 +08:00
if (!m_renderRequest.IsValid()) {
return BuildFallbackResult(
"Scene view camera is unavailable",
0.18f,
0.07f,
0.07f,
1.0f);
}
2026-04-29 01:24:21 +08:00
::XCEngine::Rendering::RenderSurface surface =
BuildViewportColorSurface(targets);
::XCEngine::Rendering::CameraFramePlan framePlan = {};
const SceneViewportFramePlanBuildStatus buildStatus =
m_engineBridge != nullptr
? m_engineBridge->BuildSceneViewportFramePlan(
2026-04-29 01:24:21 +08:00
m_renderRequest,
renderContext,
surface,
framePlan)
: SceneViewportFramePlanBuildStatus::Failed;
if (buildStatus == SceneViewportFramePlanBuildStatus::NoActiveScene) {
return BuildFallbackResult(
"No active scene",
0.07f,
0.08f,
0.10f,
1.0f);
}
2026-04-29 01:24:21 +08:00
if (buildStatus != SceneViewportFramePlanBuildStatus::Success ||
!framePlan.IsValid()) {
return BuildFallbackResult(
"Scene renderer failed",
0.18f,
0.07f,
0.07f,
1.0f);
}
ViewportRenderResult result = {};
SceneViewportRenderPlanBuildResult renderPlan =
m_renderPassBundle.BuildRenderPlan(targets, m_renderRequest);
ApplySceneViewportRenderPlan(
targets,
renderPlan.plan,
2026-04-29 01:24:21 +08:00
framePlan);
if (renderPlan.warningStatusText != nullptr) {
SetStatusIfEmpty(result.statusText, renderPlan.warningStatusText);
}
if (m_engineBridge == nullptr ||
!m_engineBridge->RenderSceneViewportFramePlan(framePlan)) {
return BuildFallbackResult(
"Scene renderer failed",
0.18f,
0.07f,
0.07f,
1.0f);
}
MarkSceneViewportRenderSuccess(
targets,
renderPlan.plan,
2026-04-29 01:24:21 +08:00
framePlan);
if (framePlan.request.objectId.IsRequested()) {
++m_objectIdFrameSerialCounter;
if (m_objectIdFrameSerialCounter == 0u) {
m_objectIdFrameSerialCounter = 1u;
}
targets.objectIdFrameSerial = m_objectIdFrameSerialCounter;
2026-04-29 01:24:21 +08:00
m_lastObjectIdRequestRevision = m_renderRequest.requestRevision;
} else {
targets.objectIdFrameSerial = 0u;
2026-04-29 01:24:21 +08:00
m_lastObjectIdRequestRevision = 0u;
}
result.rendered = true;
return result;
}
ViewportObjectIdPickResult SceneViewportRenderService::PickObject(
const ::XCEngine::UI::UISize& viewportSize,
const ::XCEngine::UI::UIPoint& viewportMousePosition) const {
if (!m_renderRequest.IsValid() ||
m_device == nullptr ||
m_lastTargets == nullptr) {
return {};
}
ViewportObjectIdPickContext pickContext = {};
pickContext.commandQueue = m_lastRenderContext.commandQueue;
pickContext.texture = m_lastTargets->objectIdTexture;
pickContext.textureState = m_lastTargets->objectIdState;
pickContext.textureWidth = m_lastTargets->width;
pickContext.textureHeight = m_lastTargets->height;
pickContext.hasValidFrame = m_lastTargets->hasValidObjectIdFrame;
pickContext.frameSerial = m_lastTargets->objectIdFrameSerial;
pickContext.viewportSize = viewportSize;
pickContext.viewportMousePosition = viewportMousePosition;
ViewportObjectIdPickResult result = PickViewportRenderObjectId(
pickContext,
[this](
const ViewportObjectIdReadbackRequest& request,
std::array<std::uint8_t, 4>& outRgba) {
return m_device->ReadTexturePixelRGBA8(
request.commandQueue,
request.texture,
request.textureState,
request.pixelX,
request.pixelY,
outRgba);
});
if (!result.HasResolvedSample() ||
!::XCEngine::Rendering::IsValidRenderObjectId(result.renderObjectId)) {
return result;
}
2026-04-29 01:24:21 +08:00
EditorSceneObjectId runtimeObjectId = kInvalidEditorSceneObjectId;
if (m_engineBridge == nullptr ||
!m_engineBridge->TryResolveActiveSceneRenderObjectId(
result.renderObjectId,
runtimeObjectId)) {
return result;
}
result.resolvedEntityId = runtimeObjectId;
return result;
}
void SceneViewportRenderService::InvalidateObjectIdFrame() {
if (m_lastTargets != nullptr) {
m_lastTargets->hasValidObjectIdFrame = false;
m_lastTargets->objectIdFrameSerial = 0u;
}
2026-04-29 01:24:21 +08:00
m_lastObjectIdRequestRevision = 0u;
}
} // namespace XCEngine::UI::Editor::App