Refactor new editor host orchestration
This commit is contained in:
@@ -127,6 +127,7 @@ add_library(XCUIEditorHost STATIC
|
||||
app/Host/D3D12WindowRenderer.cpp
|
||||
app/Host/D3D12WindowRenderLoop.cpp
|
||||
app/Host/NativeRenderer.cpp
|
||||
app/Host/WindowMessageDispatcher.cpp
|
||||
)
|
||||
|
||||
target_include_directories(XCUIEditorHost
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
#include <Host/AutoScreenshot.h>
|
||||
#include <Host/D3D12WindowRenderer.h>
|
||||
#include <Host/D3D12WindowRenderLoop.h>
|
||||
#include <Host/HostRuntimeState.h>
|
||||
#include <Host/InputModifierTracker.h>
|
||||
#include <Host/NativeRenderer.h>
|
||||
|
||||
@@ -23,6 +25,10 @@
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine::UI::Editor::Host {
|
||||
class WindowMessageDispatcher;
|
||||
}
|
||||
|
||||
namespace XCEngine::UI::Editor {
|
||||
|
||||
class Application {
|
||||
@@ -32,11 +38,15 @@ public:
|
||||
int Run(HINSTANCE hInstance, int nCmdShow);
|
||||
|
||||
private:
|
||||
friend class ::XCEngine::UI::Editor::Host::WindowMessageDispatcher;
|
||||
|
||||
static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
bool Initialize(HINSTANCE hInstance, int nCmdShow);
|
||||
void Shutdown();
|
||||
void RenderFrame();
|
||||
void OnDeferredRenderMessage();
|
||||
void OnPaintMessage();
|
||||
void OnResize();
|
||||
void OnEnterSizeMove();
|
||||
void OnExitSizeMove();
|
||||
@@ -44,7 +54,6 @@ private:
|
||||
void QueueWindowResize(UINT width, UINT height);
|
||||
void QueueCurrentClientResize();
|
||||
bool ApplyPendingWindowResize();
|
||||
void RequestDeferredRenderFrame();
|
||||
bool QueryCurrentClientPixelSize(UINT& outWidth, UINT& outHeight) const;
|
||||
bool IsPointerInsideClientArea() const;
|
||||
bool ApplyCurrentCursor() const;
|
||||
@@ -78,19 +87,14 @@ private:
|
||||
ATOM m_windowClassAtom = 0;
|
||||
::XCEngine::UI::Editor::Host::NativeRenderer m_renderer = {};
|
||||
::XCEngine::UI::Editor::Host::D3D12WindowRenderer m_windowRenderer = {};
|
||||
::XCEngine::UI::Editor::Host::D3D12WindowRenderLoop m_windowRenderLoop = {};
|
||||
::XCEngine::UI::Editor::Host::AutoScreenshotController m_autoScreenshot = {};
|
||||
::XCEngine::UI::Editor::Host::InputModifierTracker m_inputModifierTracker = {};
|
||||
App::ProductEditorContext m_editorContext = {};
|
||||
App::ProductEditorWorkspace m_editorWorkspace = {};
|
||||
std::vector<::XCEngine::UI::UIInputEvent> m_pendingInputEvents = {};
|
||||
bool m_trackingMouseLeave = false;
|
||||
UINT m_windowDpi = 96u;
|
||||
float m_dpiScale = 1.0f;
|
||||
bool m_inInteractiveResize = false;
|
||||
bool m_renderFrameQueued = false;
|
||||
bool m_hasPendingWindowResize = false;
|
||||
UINT m_pendingWindowResizeWidth = 0u;
|
||||
UINT m_pendingWindowResizeHeight = 0u;
|
||||
::XCEngine::UI::Editor::Host::HostRuntimeState m_hostRuntime = {};
|
||||
};
|
||||
|
||||
int RunXCUIEditorApp(HINSTANCE hInstance, int nCmdShow);
|
||||
|
||||
@@ -2,57 +2,115 @@
|
||||
|
||||
namespace XCEngine::UI::Editor::Host {
|
||||
|
||||
bool RenderD3D12WindowFrame(
|
||||
D3D12WindowRenderer& windowRenderer,
|
||||
const float clearColor[4],
|
||||
const D3D12WindowRenderCallback& beforePresent,
|
||||
const D3D12WindowRenderCallback& afterPresent) {
|
||||
const ::XCEngine::Rendering::RenderSurface* renderSurface =
|
||||
windowRenderer.GetCurrentRenderSurface();
|
||||
::XCEngine::Rendering::RenderContext renderContext =
|
||||
windowRenderer.GetRenderContext();
|
||||
if (!renderContext.IsValid() ||
|
||||
renderContext.commandList == nullptr ||
|
||||
renderContext.commandQueue == nullptr ||
|
||||
windowRenderer.GetSwapChain() == nullptr ||
|
||||
renderSurface == nullptr) {
|
||||
return false;
|
||||
D3D12WindowRenderLoopAttachResult D3D12WindowRenderLoop::Attach(
|
||||
NativeRenderer& uiRenderer,
|
||||
D3D12WindowRenderer& windowRenderer) {
|
||||
m_uiRenderer = &uiRenderer;
|
||||
m_windowRenderer = &windowRenderer;
|
||||
|
||||
D3D12WindowRenderLoopAttachResult result = {};
|
||||
result.hasViewportSurfacePresentation = m_uiRenderer->AttachWindowRenderer(*m_windowRenderer);
|
||||
if (!result.hasViewportSurfacePresentation) {
|
||||
const std::string& interopError = m_uiRenderer->GetLastRenderError();
|
||||
result.interopWarning = interopError.empty()
|
||||
? "native renderer d3d12 interop unavailable; falling back to hwnd renderer."
|
||||
: "native renderer d3d12 interop unavailable; falling back to hwnd renderer: " +
|
||||
interopError;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void D3D12WindowRenderLoop::Detach() {
|
||||
if (m_uiRenderer != nullptr) {
|
||||
m_uiRenderer->DetachWindowRenderer();
|
||||
}
|
||||
|
||||
auto* d3d12CommandList =
|
||||
static_cast<::XCEngine::RHI::D3D12CommandList*>(renderContext.commandList);
|
||||
if (d3d12CommandList == nullptr) {
|
||||
return false;
|
||||
m_uiRenderer = nullptr;
|
||||
m_windowRenderer = nullptr;
|
||||
}
|
||||
|
||||
D3D12WindowRenderLoopFrameContext D3D12WindowRenderLoop::BeginFrame() const {
|
||||
D3D12WindowRenderLoopFrameContext context = {};
|
||||
if (!HasViewportSurfacePresentation()) {
|
||||
return context;
|
||||
}
|
||||
|
||||
const auto& colorAttachments = renderSurface->GetColorAttachments();
|
||||
if (colorAttachments.empty() || colorAttachments[0] == nullptr) {
|
||||
return false;
|
||||
if (!m_windowRenderer->BeginFrame()) {
|
||||
const std::string& frameError = m_windowRenderer->GetLastError();
|
||||
context.warning = frameError.empty()
|
||||
? "d3d12 frame begin failed"
|
||||
: "d3d12 frame begin failed: " + frameError;
|
||||
return context;
|
||||
}
|
||||
|
||||
::XCEngine::RHI::RHIResourceView* renderTargetView = colorAttachments[0];
|
||||
renderContext.commandList->TransitionBarrier(
|
||||
renderTargetView,
|
||||
::XCEngine::RHI::ResourceStates::Present,
|
||||
::XCEngine::RHI::ResourceStates::RenderTarget);
|
||||
renderContext.commandList->SetRenderTargets(1, &renderTargetView, nullptr);
|
||||
renderContext.commandList->ClearRenderTarget(renderTargetView, clearColor);
|
||||
context.canRenderViewports = true;
|
||||
context.renderContext = m_windowRenderer->GetRenderContext();
|
||||
return context;
|
||||
}
|
||||
|
||||
if (beforePresent) {
|
||||
beforePresent(renderContext, *renderSurface);
|
||||
renderContext.commandList->SetRenderTargets(1, &renderTargetView, nullptr);
|
||||
D3D12WindowRenderLoopResizeResult D3D12WindowRenderLoop::ApplyResize(UINT width, UINT height) {
|
||||
D3D12WindowRenderLoopResizeResult result = {};
|
||||
if (m_uiRenderer == nullptr || m_windowRenderer == nullptr) {
|
||||
result.interopWarning = "window render loop is detached.";
|
||||
return result;
|
||||
}
|
||||
|
||||
if (afterPresent) {
|
||||
afterPresent(renderContext, *renderSurface);
|
||||
renderContext.commandList->SetRenderTargets(1, &renderTargetView, nullptr);
|
||||
m_uiRenderer->Resize(width, height);
|
||||
m_uiRenderer->DetachWindowRenderer();
|
||||
|
||||
const bool resizedWindowRenderer =
|
||||
m_windowRenderer->Resize(static_cast<int>(width), static_cast<int>(height));
|
||||
if (!resizedWindowRenderer || !m_windowRenderer->GetLastError().empty()) {
|
||||
const std::string& resizeError = m_windowRenderer->GetLastError();
|
||||
result.windowRendererWarning = resizeError.empty()
|
||||
? "window renderer resize warning."
|
||||
: "window renderer resize warning: " + resizeError;
|
||||
}
|
||||
|
||||
renderContext.commandList->TransitionBarrier(
|
||||
renderTargetView,
|
||||
::XCEngine::RHI::ResourceStates::RenderTarget,
|
||||
::XCEngine::RHI::ResourceStates::Present);
|
||||
return windowRenderer.SubmitFrame(true);
|
||||
if (!resizedWindowRenderer) {
|
||||
return result;
|
||||
}
|
||||
|
||||
const D3D12WindowRenderLoopAttachResult attachResult =
|
||||
Attach(*m_uiRenderer, *m_windowRenderer);
|
||||
result.hasViewportSurfacePresentation = attachResult.hasViewportSurfacePresentation;
|
||||
result.interopWarning = attachResult.interopWarning;
|
||||
return result;
|
||||
}
|
||||
|
||||
D3D12WindowRenderLoopPresentResult D3D12WindowRenderLoop::Present(
|
||||
const ::XCEngine::UI::UIDrawData& drawData) const {
|
||||
D3D12WindowRenderLoopPresentResult result = {};
|
||||
if (m_uiRenderer == nullptr) {
|
||||
result.warning = "window render loop has no ui renderer.";
|
||||
return result;
|
||||
}
|
||||
|
||||
if (HasViewportSurfacePresentation()) {
|
||||
result.framePresented = m_uiRenderer->RenderToWindowRenderer(drawData);
|
||||
if (!result.framePresented) {
|
||||
const std::string& composeError = m_uiRenderer->GetLastRenderError();
|
||||
result.warning = composeError.empty()
|
||||
? "d3d12 window composition failed, falling back to hwnd renderer."
|
||||
: "d3d12 window composition failed, falling back to hwnd renderer: " +
|
||||
composeError;
|
||||
}
|
||||
}
|
||||
|
||||
if (!result.framePresented) {
|
||||
result.framePresented = m_uiRenderer->Render(drawData);
|
||||
if (!result.framePresented && result.warning.empty()) {
|
||||
result.warning = m_uiRenderer->GetLastRenderError();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool D3D12WindowRenderLoop::HasViewportSurfacePresentation() const {
|
||||
return m_uiRenderer != nullptr &&
|
||||
m_windowRenderer != nullptr &&
|
||||
m_uiRenderer->HasAttachedWindowRenderer();
|
||||
}
|
||||
|
||||
} // namespace XCEngine::UI::Editor::Host
|
||||
|
||||
@@ -1,20 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#include "D3D12WindowRenderer.h"
|
||||
#include "NativeRenderer.h"
|
||||
|
||||
#include <functional>
|
||||
#include <XCEngine/UI/DrawData.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace XCEngine::UI::Editor::Host {
|
||||
|
||||
using D3D12WindowRenderCallback =
|
||||
std::function<void(
|
||||
const ::XCEngine::Rendering::RenderContext&,
|
||||
const ::XCEngine::Rendering::RenderSurface&)>;
|
||||
struct D3D12WindowRenderLoopAttachResult {
|
||||
bool hasViewportSurfacePresentation = false;
|
||||
std::string interopWarning = {};
|
||||
};
|
||||
|
||||
bool RenderD3D12WindowFrame(
|
||||
D3D12WindowRenderer& windowRenderer,
|
||||
const float clearColor[4],
|
||||
const D3D12WindowRenderCallback& beforePresent = {},
|
||||
const D3D12WindowRenderCallback& afterPresent = {});
|
||||
struct D3D12WindowRenderLoopFrameContext {
|
||||
bool canRenderViewports = false;
|
||||
::XCEngine::Rendering::RenderContext renderContext = {};
|
||||
std::string warning = {};
|
||||
};
|
||||
|
||||
struct D3D12WindowRenderLoopResizeResult {
|
||||
bool hasViewportSurfacePresentation = false;
|
||||
std::string windowRendererWarning = {};
|
||||
std::string interopWarning = {};
|
||||
};
|
||||
|
||||
struct D3D12WindowRenderLoopPresentResult {
|
||||
bool framePresented = false;
|
||||
std::string warning = {};
|
||||
};
|
||||
|
||||
class D3D12WindowRenderLoop {
|
||||
public:
|
||||
D3D12WindowRenderLoopAttachResult Attach(
|
||||
NativeRenderer& uiRenderer,
|
||||
D3D12WindowRenderer& windowRenderer);
|
||||
void Detach();
|
||||
|
||||
D3D12WindowRenderLoopFrameContext BeginFrame() const;
|
||||
D3D12WindowRenderLoopResizeResult ApplyResize(UINT width, UINT height);
|
||||
D3D12WindowRenderLoopPresentResult Present(
|
||||
const ::XCEngine::UI::UIDrawData& drawData) const;
|
||||
|
||||
bool HasViewportSurfacePresentation() const;
|
||||
|
||||
private:
|
||||
NativeRenderer* m_uiRenderer = nullptr;
|
||||
D3D12WindowRenderer* m_windowRenderer = nullptr;
|
||||
};
|
||||
|
||||
} // namespace XCEngine::UI::Editor::Host
|
||||
|
||||
89
new_editor/app/Host/HostRuntimeState.h
Normal file
89
new_editor/app/Host/HostRuntimeState.h
Normal file
@@ -0,0 +1,89 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
namespace XCEngine::UI::Editor::Host {
|
||||
|
||||
class HostRuntimeState {
|
||||
public:
|
||||
void Reset() {
|
||||
m_windowDpi = 96u;
|
||||
m_inInteractiveResize = false;
|
||||
m_renderFrameQueued = false;
|
||||
m_hasPendingWindowResize = false;
|
||||
m_pendingWindowResizeWidth = 0u;
|
||||
m_pendingWindowResizeHeight = 0u;
|
||||
}
|
||||
|
||||
void SetWindowDpi(UINT dpi) {
|
||||
m_windowDpi = dpi == 0u ? 96u : dpi;
|
||||
}
|
||||
|
||||
UINT GetWindowDpi() const {
|
||||
return m_windowDpi;
|
||||
}
|
||||
|
||||
float GetDpiScale(float baseDpiScale) const {
|
||||
return baseDpiScale > 0.0f
|
||||
? static_cast<float>(m_windowDpi) / baseDpiScale
|
||||
: 1.0f;
|
||||
}
|
||||
|
||||
void BeginInteractiveResize() {
|
||||
m_inInteractiveResize = true;
|
||||
}
|
||||
|
||||
void EndInteractiveResize() {
|
||||
m_inInteractiveResize = false;
|
||||
}
|
||||
|
||||
bool IsInteractiveResize() const {
|
||||
return m_inInteractiveResize;
|
||||
}
|
||||
|
||||
void QueueWindowResize(UINT width, UINT height) {
|
||||
if (width == 0u || height == 0u) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_pendingWindowResizeWidth = width;
|
||||
m_pendingWindowResizeHeight = height;
|
||||
m_hasPendingWindowResize = true;
|
||||
}
|
||||
|
||||
bool ConsumePendingWindowResize(UINT& outWidth, UINT& outHeight) {
|
||||
outWidth = 0u;
|
||||
outHeight = 0u;
|
||||
if (!m_hasPendingWindowResize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_hasPendingWindowResize = false;
|
||||
outWidth = m_pendingWindowResizeWidth;
|
||||
outHeight = m_pendingWindowResizeHeight;
|
||||
return outWidth > 0u && outHeight > 0u;
|
||||
}
|
||||
|
||||
bool TryQueueDeferredRender() {
|
||||
if (m_renderFrameQueued) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_renderFrameQueued = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClearDeferredRenderRequest() {
|
||||
m_renderFrameQueued = false;
|
||||
}
|
||||
|
||||
private:
|
||||
UINT m_windowDpi = 96u;
|
||||
bool m_inInteractiveResize = false;
|
||||
bool m_renderFrameQueued = false;
|
||||
bool m_hasPendingWindowResize = false;
|
||||
UINT m_pendingWindowResizeWidth = 0u;
|
||||
UINT m_pendingWindowResizeHeight = 0u;
|
||||
};
|
||||
|
||||
} // namespace XCEngine::UI::Editor::Host
|
||||
115
new_editor/app/Host/WindowMessageDispatcher.cpp
Normal file
115
new_editor/app/Host/WindowMessageDispatcher.cpp
Normal file
@@ -0,0 +1,115 @@
|
||||
#include "WindowMessageDispatcher.h"
|
||||
|
||||
#include "../Application.h"
|
||||
|
||||
namespace XCEngine::UI::Editor::Host {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr UINT kDeferredRenderMessage = WM_APP + 1u;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Application* WindowMessageDispatcher::GetApplicationFromWindow(HWND hwnd) {
|
||||
return reinterpret_cast<Application*>(GetWindowLongPtrW(hwnd, GWLP_USERDATA));
|
||||
}
|
||||
|
||||
bool WindowMessageDispatcher::TryHandleNonClientCreate(
|
||||
HWND hwnd,
|
||||
UINT message,
|
||||
LPARAM lParam,
|
||||
LRESULT& outResult) {
|
||||
if (message != WM_NCCREATE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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));
|
||||
outResult = TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WindowMessageDispatcher::TryDispatch(
|
||||
Application& application,
|
||||
UINT message,
|
||||
WPARAM wParam,
|
||||
LPARAM lParam,
|
||||
LRESULT& outResult) {
|
||||
switch (message) {
|
||||
case WM_SETCURSOR:
|
||||
if (LOWORD(lParam) == HTCLIENT && application.ApplyCurrentCursor()) {
|
||||
outResult = TRUE;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case WM_DPICHANGED:
|
||||
if (lParam == 0) {
|
||||
return false;
|
||||
}
|
||||
application.OnDpiChanged(
|
||||
static_cast<UINT>(LOWORD(wParam)),
|
||||
*reinterpret_cast<const RECT*>(lParam));
|
||||
RequestDeferredRenderFrame(application);
|
||||
outResult = 0;
|
||||
return true;
|
||||
case WM_ENTERSIZEMOVE:
|
||||
application.OnEnterSizeMove();
|
||||
outResult = 0;
|
||||
return true;
|
||||
case WM_EXITSIZEMOVE:
|
||||
application.OnExitSizeMove();
|
||||
RequestDeferredRenderFrame(application);
|
||||
outResult = 0;
|
||||
return true;
|
||||
case WM_SIZE:
|
||||
if (wParam != SIZE_MINIMIZED) {
|
||||
application.OnResize();
|
||||
RequestDeferredRenderFrame(application);
|
||||
}
|
||||
outResult = 0;
|
||||
return true;
|
||||
case kDeferredRenderMessage:
|
||||
application.OnDeferredRenderMessage();
|
||||
outResult = 0;
|
||||
return true;
|
||||
case WM_PAINT:
|
||||
application.OnPaintMessage();
|
||||
outResult = 0;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void WindowMessageDispatcher::RequestDeferredRenderFrame(Application& application) {
|
||||
if (application.m_hwnd == nullptr ||
|
||||
!IsWindow(application.m_hwnd) ||
|
||||
!application.m_hostRuntime.TryQueueDeferredRender()) {
|
||||
return;
|
||||
}
|
||||
|
||||
PostMessageW(application.m_hwnd, kDeferredRenderMessage, 0, 0);
|
||||
}
|
||||
|
||||
} // namespace XCEngine::UI::Editor::Host
|
||||
34
new_editor/app/Host/WindowMessageDispatcher.h
Normal file
34
new_editor/app/Host/WindowMessageDispatcher.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
namespace XCEngine::UI::Editor {
|
||||
class Application;
|
||||
}
|
||||
|
||||
namespace XCEngine::UI::Editor::Host {
|
||||
|
||||
class WindowMessageDispatcher {
|
||||
public:
|
||||
static Application* GetApplicationFromWindow(HWND hwnd);
|
||||
static bool TryHandleNonClientCreate(
|
||||
HWND hwnd,
|
||||
UINT message,
|
||||
LPARAM lParam,
|
||||
LRESULT& outResult);
|
||||
static bool TryDispatch(
|
||||
Application& application,
|
||||
UINT message,
|
||||
WPARAM wParam,
|
||||
LPARAM lParam,
|
||||
LRESULT& outResult);
|
||||
|
||||
private:
|
||||
static void RequestDeferredRenderFrame(Application& application);
|
||||
};
|
||||
|
||||
} // namespace XCEngine::UI::Editor::Host
|
||||
Reference in New Issue
Block a user