Refactor new editor host orchestration

This commit is contained in:
2026-04-13 19:37:10 +08:00
parent d2140bf5cc
commit f3fc34898a
9 changed files with 648 additions and 223 deletions

View File

@@ -1,5 +1,7 @@
#include "Application.h"
#include <Host/WindowMessageDispatcher.h>
#include <XCEditor/Foundation/UIEditorRuntimeTrace.h>
#include <XCEditor/Foundation/UIEditorTheme.h>
@@ -36,7 +38,6 @@ constexpr const wchar_t* kWindowClassName = L"XCEditorShellHost";
constexpr const wchar_t* kWindowTitle = L"Main Scene * - Main.xx - XCEngine Editor";
constexpr UINT kDefaultDpi = 96u;
constexpr float kBaseDpiScale = 96.0f;
constexpr UINT kDeferredRenderMessage = WM_APP + 1u;
bool ResolveVerboseRuntimeTraceEnabled() {
wchar_t buffer[8] = {};
@@ -47,10 +48,6 @@ bool ResolveVerboseRuntimeTraceEnabled() {
return length > 0u && buffer[0] != L'0';
}
Application* GetApplicationFromWindow(HWND hwnd) {
return reinterpret_cast<Application*>(GetWindowLongPtrW(hwnd, GWLP_USERDATA));
}
UINT QuerySystemDpi() {
HDC screenDc = GetDC(nullptr);
if (screenDc == nullptr) {
@@ -131,25 +128,6 @@ void EnableDpiAwareness() {
}
}
void TryEnableNonClientDpiScaling(HWND hwnd) {
if (hwnd == nullptr) {
return;
}
const HMODULE user32 = GetModuleHandleW(L"user32.dll");
if (user32 == nullptr) {
return;
}
using EnableNonClientDpiScalingFn = BOOL(WINAPI*)(HWND);
const auto enableNonClientDpiScaling =
reinterpret_cast<EnableNonClientDpiScalingFn>(
GetProcAddress(user32, "EnableNonClientDpiScaling"));
if (enableNonClientDpiScaling != nullptr) {
enableNonClientDpiScaling(hwnd);
}
}
std::string TruncateText(const std::string& text, std::size_t maxLength) {
if (text.size() <= maxLength) {
return text;
@@ -456,9 +434,9 @@ bool Application::Initialize(HINSTANCE hInstance, int nCmdShow) {
LogRuntimeTrace("app", "window creation failed");
return false;
}
m_windowDpi = QueryWindowDpi(m_hwnd);
m_dpiScale = GetDpiScale();
m_renderer.SetDpiScale(m_dpiScale);
m_hostRuntime.Reset();
m_hostRuntime.SetWindowDpi(QueryWindowDpi(m_hwnd));
m_renderer.SetDpiScale(GetDpiScale());
m_editorContext.SetExitRequestHandler([this]() {
if (m_hwnd != nullptr) {
PostMessageW(m_hwnd, WM_CLOSE, 0, 0);
@@ -466,7 +444,7 @@ bool Application::Initialize(HINSTANCE hInstance, int nCmdShow) {
});
std::ostringstream dpiTrace = {};
dpiTrace << "initial dpi=" << m_windowDpi << " scale=" << m_dpiScale;
dpiTrace << "initial dpi=" << m_hostRuntime.GetWindowDpi() << " scale=" << GetDpiScale();
LogRuntimeTrace("window", dpiTrace.str());
if (!m_renderer.Initialize(m_hwnd)) {
@@ -481,17 +459,18 @@ bool Application::Initialize(HINSTANCE hInstance, int nCmdShow) {
LogRuntimeTrace("app", "d3d12 window renderer initialization failed");
return false;
}
const bool hasD3D12WindowInterop = m_renderer.AttachWindowRenderer(m_windowRenderer);
if (!hasD3D12WindowInterop) {
const Host::D3D12WindowRenderLoopAttachResult attachResult =
m_windowRenderLoop.Attach(m_renderer, m_windowRenderer);
if (!attachResult.interopWarning.empty()) {
LogRuntimeTrace(
"app",
"native renderer d3d12 interop unavailable; falling back to hwnd renderer: " +
m_renderer.GetLastRenderError());
attachResult.interopWarning);
}
m_editorContext.AttachTextMeasurer(m_renderer);
m_editorWorkspace.Initialize(repoRoot, m_renderer);
m_editorWorkspace.AttachViewportWindowRenderer(m_windowRenderer);
m_editorWorkspace.SetViewportSurfacePresentationEnabled(hasD3D12WindowInterop);
m_editorWorkspace.SetViewportSurfacePresentationEnabled(
attachResult.hasViewportSurfacePresentation);
if (!m_editorWorkspace.GetBuiltInIconError().empty()) {
LogRuntimeTrace("icons", m_editorWorkspace.GetBuiltInIconError());
}
@@ -520,6 +499,7 @@ void Application::Shutdown() {
m_autoScreenshot.Shutdown();
m_editorWorkspace.Shutdown();
m_windowRenderLoop.Detach();
m_windowRenderer.Shutdown();
m_renderer.Shutdown();
@@ -570,11 +550,10 @@ void Application::RenderFrame() {
m_editorWorkspace.GetShellInteractionState()));
}
const bool canUseWindowRenderer = m_renderer.HasAttachedWindowRenderer();
const bool d3d12FrameBegun =
canUseWindowRenderer && m_windowRenderer.BeginFrame();
if (canUseWindowRenderer && !d3d12FrameBegun) {
LogRuntimeTrace("viewport", "d3d12 frame begin failed");
const Host::D3D12WindowRenderLoopFrameContext frameContext =
m_windowRenderLoop.BeginFrame();
if (!frameContext.warning.empty()) {
LogRuntimeTrace("viewport", frameContext.warning);
}
m_editorWorkspace.Update(
@@ -610,8 +589,8 @@ void Application::RenderFrame() {
ApplyHostedContentCaptureRequests();
ApplyCurrentCursor();
m_editorWorkspace.Append(drawList);
if (d3d12FrameBegun) {
m_editorWorkspace.RenderRequestedViewports(m_windowRenderer.GetRenderContext());
if (frameContext.canRenderViewports) {
m_editorWorkspace.RenderRequestedViewports(frameContext.renderContext);
}
} else {
drawList.AddText(
@@ -628,31 +607,37 @@ void Application::RenderFrame() {
12.0f);
}
bool framePresented = false;
if (m_renderer.HasAttachedWindowRenderer()) {
framePresented = m_renderer.RenderToWindowRenderer(drawData);
if (!framePresented) {
LogRuntimeTrace(
"present",
"d3d12 window composition failed, falling back to hwnd renderer: " +
m_renderer.GetLastRenderError());
}
}
if (!framePresented) {
framePresented = m_renderer.Render(drawData);
const Host::D3D12WindowRenderLoopPresentResult presentResult =
m_windowRenderLoop.Present(drawData);
if (!presentResult.warning.empty()) {
LogRuntimeTrace("present", presentResult.warning);
}
m_autoScreenshot.CaptureIfRequested(
m_renderer,
drawData,
pixelWidth,
pixelHeight,
framePresented);
presentResult.framePresented);
}
void Application::OnDeferredRenderMessage() {
m_hostRuntime.ClearDeferredRenderRequest();
RenderFrame();
}
void Application::OnPaintMessage() {
if (m_hwnd == nullptr) {
return;
}
PAINTSTRUCT paintStruct = {};
BeginPaint(m_hwnd, &paintStruct);
RenderFrame();
EndPaint(m_hwnd, &paintStruct);
}
float Application::GetDpiScale() const {
const UINT dpi = m_windowDpi == 0u ? kDefaultDpi : m_windowDpi;
return static_cast<float>(dpi) / kBaseDpiScale;
return m_hostRuntime.GetDpiScale(kBaseDpiScale);
}
float Application::PixelsToDips(float pixels) const {
@@ -767,22 +752,16 @@ void Application::OnResize() {
}
void Application::OnEnterSizeMove() {
m_inInteractiveResize = true;
m_hostRuntime.BeginInteractiveResize();
}
void Application::OnExitSizeMove() {
m_inInteractiveResize = false;
m_hostRuntime.EndInteractiveResize();
QueueCurrentClientResize();
}
void Application::QueueWindowResize(UINT width, UINT height) {
if (width == 0u || height == 0u) {
return;
}
m_pendingWindowResizeWidth = width;
m_pendingWindowResizeHeight = height;
m_hasPendingWindowResize = true;
m_hostRuntime.QueueWindowResize(width, height);
}
void Application::QueueCurrentClientResize() {
@@ -796,51 +775,26 @@ void Application::QueueCurrentClientResize() {
}
bool Application::ApplyPendingWindowResize() {
if (!m_hasPendingWindowResize) {
UINT width = 0u;
UINT height = 0u;
if (!m_hostRuntime.ConsumePendingWindowResize(width, height)) {
return true;
}
const UINT width = m_pendingWindowResizeWidth;
const UINT height = m_pendingWindowResizeHeight;
m_hasPendingWindowResize = false;
if (width == 0u || height == 0u) {
return false;
const Host::D3D12WindowRenderLoopResizeResult resizeResult =
m_windowRenderLoop.ApplyResize(width, height);
m_editorWorkspace.SetViewportSurfacePresentationEnabled(
resizeResult.hasViewportSurfacePresentation);
if (!resizeResult.windowRendererWarning.empty()) {
LogRuntimeTrace("present", resizeResult.windowRendererWarning);
}
m_renderer.Resize(width, height);
m_renderer.DetachWindowRenderer();
const bool resizedWindowRenderer =
m_windowRenderer.Resize(static_cast<int>(width), static_cast<int>(height));
const bool hasD3D12WindowInterop = resizedWindowRenderer &&
m_renderer.AttachWindowRenderer(m_windowRenderer);
const bool hasHealthyD3D12WindowInterop =
resizedWindowRenderer &&
hasD3D12WindowInterop;
m_editorWorkspace.SetViewportSurfacePresentationEnabled(hasHealthyD3D12WindowInterop);
if (!resizedWindowRenderer || !m_windowRenderer.GetLastError().empty()) {
LogRuntimeTrace(
"present",
"window renderer resize warning: " + m_windowRenderer.GetLastError());
if (!resizeResult.interopWarning.empty()) {
LogRuntimeTrace("present", resizeResult.interopWarning);
}
if (!hasD3D12WindowInterop) {
LogRuntimeTrace(
"present",
"failed to rebuild d3d12 window interop after resize: " +
m_renderer.GetLastRenderError());
}
return hasHealthyD3D12WindowInterop;
}
void Application::RequestDeferredRenderFrame() {
if (m_hwnd == nullptr || !IsWindow(m_hwnd) || m_renderFrameQueued) {
return;
}
m_renderFrameQueued = true;
PostMessageW(m_hwnd, kDeferredRenderMessage, 0, 0);
return resizeResult.hasViewportSurfacePresentation;
}
bool Application::QueryCurrentClientPixelSize(UINT& outWidth, UINT& outHeight) const {
@@ -867,9 +821,8 @@ bool Application::QueryCurrentClientPixelSize(UINT& outWidth, UINT& outHeight) c
}
void Application::OnDpiChanged(UINT dpi, const RECT& suggestedRect) {
m_windowDpi = dpi == 0u ? kDefaultDpi : dpi;
m_dpiScale = GetDpiScale();
m_renderer.SetDpiScale(m_dpiScale);
m_hostRuntime.SetWindowDpi(dpi == 0u ? kDefaultDpi : dpi);
m_renderer.SetDpiScale(GetDpiScale());
if (m_hwnd != nullptr) {
const LONG windowWidth = suggestedRect.right - suggestedRect.left;
const LONG windowHeight = suggestedRect.bottom - suggestedRect.top;
@@ -882,11 +835,10 @@ void Application::OnDpiChanged(UINT dpi, const RECT& suggestedRect) {
windowHeight,
SWP_NOZORDER | SWP_NOACTIVATE);
QueueCurrentClientResize();
RequestDeferredRenderFrame();
}
std::ostringstream trace = {};
trace << "dpi changed to " << m_windowDpi << " scale=" << m_dpiScale;
trace << "dpi changed to " << m_hostRuntime.GetWindowDpi() << " scale=" << GetDpiScale();
LogRuntimeTrace("window", trace.str());
}
@@ -1006,66 +958,27 @@ LONG WINAPI Application::HandleUnhandledException(EXCEPTION_POINTERS* exceptionI
}
LRESULT CALLBACK Application::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
if (message == WM_NCCREATE) {
TryEnableNonClientDpiScaling(hwnd);
const auto* createStruct = reinterpret_cast<CREATESTRUCTW*>(lParam);
auto* application = reinterpret_cast<Application*>(createStruct->lpCreateParams);
SetWindowLongPtrW(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(application));
return TRUE;
LRESULT dispatcherResult = 0;
if (Host::WindowMessageDispatcher::TryHandleNonClientCreate(
hwnd,
message,
lParam,
dispatcherResult)) {
return dispatcherResult;
}
Application* application = Host::WindowMessageDispatcher::GetApplicationFromWindow(hwnd);
if (application != nullptr &&
Host::WindowMessageDispatcher::TryDispatch(
*application,
message,
wParam,
lParam,
dispatcherResult)) {
return dispatcherResult;
}
Application* application = GetApplicationFromWindow(hwnd);
switch (message) {
case WM_SETCURSOR:
if (application != nullptr &&
LOWORD(lParam) == HTCLIENT &&
application->ApplyCurrentCursor()) {
return TRUE;
}
break;
case WM_DPICHANGED:
if (application != nullptr && lParam != 0) {
application->OnDpiChanged(
static_cast<UINT>(LOWORD(wParam)),
*reinterpret_cast<RECT*>(lParam));
return 0;
}
break;
case WM_ENTERSIZEMOVE:
if (application != nullptr) {
application->OnEnterSizeMove();
return 0;
}
break;
case WM_EXITSIZEMOVE:
if (application != nullptr) {
application->OnExitSizeMove();
application->RequestDeferredRenderFrame();
return 0;
}
break;
case WM_SIZE:
if (application != nullptr && wParam != SIZE_MINIMIZED) {
application->OnResize();
application->RequestDeferredRenderFrame();
}
return 0;
case kDeferredRenderMessage:
if (application != nullptr) {
application->m_renderFrameQueued = false;
application->RenderFrame();
return 0;
}
break;
case WM_PAINT:
if (application != nullptr) {
PAINTSTRUCT paintStruct = {};
BeginPaint(hwnd, &paintStruct);
application->RenderFrame();
EndPaint(hwnd, &paintStruct);
return 0;
}
break;
case WM_MOUSEMOVE:
if (application != nullptr) {
if (!application->m_trackingMouseLeave) {