checkpoint(new_editor): native d3d12 ui path

Key node 1: move main-window UI presentation onto the D3D12 render loop with native UI renderer, text system, and texture host.

Key node 2: wire frame timing/FPS display, window runtime, swapchain presentation, and native screenshot capture around the new path.

Key node 3: carry editor shell/workspace/viewport/panel interaction updates needed by the new renderer and detached window flow.

Key node 4: pump async resource loads and scene bridge follow-up needed for scene content visibility in new_editor.
This commit is contained in:
2026-04-21 20:49:18 +08:00
parent a779b04dba
commit 6e9265e92e
53 changed files with 5330 additions and 858 deletions

View File

@@ -8,6 +8,7 @@
#include <algorithm>
#include <chrono>
#include <cstdio>
#include <cmath>
#include <utility>
namespace XCEngine::UI::Editor::App {
@@ -18,6 +19,7 @@ using namespace EditorWindowSupport;
namespace {
constexpr float kFrameTimeSmoothingFactor = 0.12f;
constexpr float kFrameStatsDisplayRefreshIntervalSeconds = 0.25f;
}
@@ -73,14 +75,17 @@ void EditorWindowRuntimeController::ClearExternalDockHostDropPreview() {
void EditorWindowRuntimeController::SetDpiScale(float dpiScale) {
m_renderer.SetDpiScale(dpiScale);
m_textSystem.SetDpiScale(dpiScale);
m_uiRenderer.SetDpiScale(dpiScale);
}
Host::NativeRenderer& EditorWindowRuntimeController::GetRenderer() {
return m_renderer;
::XCEngine::UI::Editor::UIEditorTextMeasurer& EditorWindowRuntimeController::GetTextMeasurer() {
return m_textSystem;
}
const Host::NativeRenderer& EditorWindowRuntimeController::GetRenderer() const {
return m_renderer;
const ::XCEngine::UI::Editor::UIEditorTextMeasurer&
EditorWindowRuntimeController::GetTextMeasurer() const {
return m_textSystem;
}
const ::XCEngine::UI::UITextureHandle& EditorWindowRuntimeController::GetTitleBarLogoIcon() const {
@@ -98,36 +103,53 @@ bool EditorWindowRuntimeController::Initialize(
return false;
}
if (!m_renderer.Initialize(hwnd)) {
LogRuntimeTrace("app", "renderer initialization failed");
return false;
}
RECT clientRect = {};
GetClientRect(hwnd, &clientRect);
const int clientWidth = (std::max)(clientRect.right - clientRect.left, 1L);
const int clientHeight = (std::max)(clientRect.bottom - clientRect.top, 1L);
if (!m_windowRenderer.Initialize(hwnd, clientWidth, clientHeight)) {
LogRuntimeTrace("app", "d3d12 window renderer initialization failed");
m_renderer.Shutdown();
return false;
}
const Host::D3D12WindowRenderLoopAttachResult attachResult =
m_windowRenderLoop.Attach(m_renderer, m_windowRenderer);
if (!attachResult.interopWarning.empty()) {
LogRuntimeTrace("app", attachResult.interopWarning);
if (!m_textureHost.Initialize(m_windowRenderer)) {
LogRuntimeTrace("app", "d3d12 ui texture host initialization failed");
m_windowRenderer.Shutdown();
return false;
}
editorContext.AttachTextMeasurer(m_renderer);
m_shellRuntime.Initialize(repoRoot, m_renderer, m_renderer);
if (!m_textSystem.Initialize()) {
LogRuntimeTrace("app", "d3d12 ui text system initialization failed");
m_textureHost.Shutdown();
m_windowRenderer.Shutdown();
return false;
}
m_textSystem.SetDpiScale(m_renderer.GetDpiScale());
if (!m_uiRenderer.Initialize(m_windowRenderer, m_textureHost, m_textSystem)) {
LogRuntimeTrace("app", "d3d12 ui renderer initialization failed");
m_textSystem.Shutdown();
m_textureHost.Shutdown();
m_windowRenderer.Shutdown();
return false;
}
m_uiRenderer.SetDpiScale(m_renderer.GetDpiScale());
const Host::D3D12WindowRenderLoopAttachResult attachResult =
m_windowRenderLoop.Attach(m_uiRenderer, m_windowRenderer);
if (!attachResult.warning.empty()) {
LogRuntimeTrace("app", attachResult.warning);
}
editorContext.AttachTextMeasurer(m_textSystem);
m_shellRuntime.Initialize(repoRoot, m_textureHost, m_textSystem);
m_shellRuntime.AttachViewportWindowRenderer(m_windowRenderer);
m_shellRuntime.SetViewportSurfacePresentationEnabled(
attachResult.hasViewportSurfacePresentation);
std::string titleBarLogoError = {};
if (!LoadEmbeddedPngTexture(
m_renderer,
m_textureHost,
IDR_PNG_LOGO_ICON,
m_titleBarLogoIcon,
titleBarLogoError)) {
@@ -160,8 +182,11 @@ void EditorWindowRuntimeController::Shutdown() {
ResetFrameTiming();
m_autoScreenshot.Shutdown();
m_shellRuntime.Shutdown();
m_renderer.ReleaseTexture(m_titleBarLogoIcon);
m_windowRenderLoop.Detach();
m_uiRenderer.Shutdown();
m_textSystem.Shutdown();
m_textureHost.ReleaseTexture(m_titleBarLogoIcon);
m_textureHost.Shutdown();
m_windowRenderer.Shutdown();
m_renderer.Shutdown();
}
@@ -185,10 +210,6 @@ bool EditorWindowRuntimeController::ApplyResize(UINT width, UINT height) {
LogRuntimeTrace("present", resizeResult.windowRendererWarning);
}
if (!resizeResult.interopWarning.empty()) {
LogRuntimeTrace("present", resizeResult.interopWarning);
}
return resizeResult.hasViewportSurfacePresentation;
}
@@ -209,6 +230,7 @@ void EditorWindowRuntimeController::CaptureIfRequested(
bool framePresented) {
m_autoScreenshot.CaptureIfRequested(
m_renderer,
m_windowRenderer,
drawData,
pixelWidth,
pixelHeight,
@@ -236,26 +258,17 @@ std::string EditorWindowRuntimeController::BuildCaptureStatusText() const {
}
std::string EditorWindowRuntimeController::BuildFrameRateText() const {
if (m_displayFps <= 0.0f || m_displayFrameTimeMs <= 0.0f) {
return {};
}
char buffer[48] = {};
std::snprintf(
buffer,
sizeof(buffer),
"FPS %.1f | %.2f ms",
m_displayFps,
m_displayFrameTimeMs);
return buffer;
return m_frameRateText;
}
void EditorWindowRuntimeController::ResetFrameTiming() {
m_lastFrameTime = {};
m_hasLastFrameTime = false;
m_smoothedDeltaTimeSeconds = 0.0f;
m_frameStatsDisplayAccumulatorSeconds = 0.0f;
m_displayFps = 0.0f;
m_displayFrameTimeMs = 0.0f;
m_frameRateText.clear();
}
void EditorWindowRuntimeController::UpdateFrameTiming() {
@@ -283,6 +296,33 @@ void EditorWindowRuntimeController::UpdateFrameTiming() {
m_displayFps = m_smoothedDeltaTimeSeconds > 0.0f
? 1.0f / m_smoothedDeltaTimeSeconds
: 0.0f;
m_frameStatsDisplayAccumulatorSeconds += deltaTime;
if (m_frameRateText.empty() ||
m_frameStatsDisplayAccumulatorSeconds >= kFrameStatsDisplayRefreshIntervalSeconds) {
RefreshDisplayedFrameStats();
m_frameStatsDisplayAccumulatorSeconds = 0.0f;
}
}
void EditorWindowRuntimeController::RefreshDisplayedFrameStats() {
if (m_displayFps <= 0.0f || m_displayFrameTimeMs <= 0.0f) {
m_frameRateText.clear();
return;
}
const int roundedFps = (std::max)(0, static_cast<int>(std::lround(m_displayFps)));
const int roundedFrameTimeMs =
(std::max)(0, static_cast<int>(std::lround(m_displayFrameTimeMs)));
char buffer[48] = {};
std::snprintf(
buffer,
sizeof(buffer),
"FPS %3d | %2d ms",
roundedFps,
roundedFrameTimeMs);
m_frameRateText = buffer;
}
} // namespace XCEngine::UI::Editor::App