From 09b1289338ea6c335762f02cfa5c77ed15e1a33a Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Sun, 26 Apr 2026 23:30:29 +0800 Subject: [PATCH] Seal editor Win32 platform boundary --- editor/AGENTS.md | 72 +++++-- editor/CMakeLists.txt | 2 + editor/app/Bootstrap/Application.cpp | 12 +- .../Win32/Diagnostics/Win32CrashTrace.cpp | 186 ++++++++++++++++ .../Win32/Diagnostics/Win32CrashTrace.h | 13 ++ .../Platform/Win32/Windowing/EditorWindow.cpp | 5 +- .../Windowing/EditorWindowHostRuntime.cpp | 19 +- .../Win32EditorWindowRenderRuntimeSurface.h | 33 +++ .../D3D12/D3D12EditorWindowRenderRuntime.cpp | 17 +- .../D3D12/D3D12EditorWindowRenderRuntime.h | 2 +- .../Host/EditorWindowRenderRuntime.h | 22 +- .../EditorUtilityWindowRegistry.cpp | 16 +- .../EditorUtilityWindowRegistry.h | 2 +- .../EditorUtilityWindowCoordinator.cpp | 2 +- editor/app/Windowing/EditorWindowInstance.cpp | 4 +- .../Host/EditorWindowHostInterfaces.h | 9 +- .../Windowing/Host/EditorWindowHostTypes.h | 2 +- editor/app/Windowing/Host/EditorWindowTypes.h | 11 +- .../Runtime/EditorWindowRuntimeController.cpp | 4 +- .../Runtime/EditorWindowRuntimeController.h | 2 +- .../Foundation/UIEditorRuntimeTrace.h | 25 ++- .../src/Foundation/UIEditorRuntimeTrace.cpp | 199 +++--------------- 22 files changed, 410 insertions(+), 249 deletions(-) create mode 100644 editor/app/Platform/Win32/Diagnostics/Win32CrashTrace.cpp create mode 100644 editor/app/Platform/Win32/Diagnostics/Win32CrashTrace.h create mode 100644 editor/app/Platform/Win32/Windowing/Win32EditorWindowRenderRuntimeSurface.h diff --git a/editor/AGENTS.md b/editor/AGENTS.md index 4248a22c..551c2d59 100644 --- a/editor/AGENTS.md +++ b/editor/AGENTS.md @@ -55,7 +55,8 @@ Use these ownership boundaries when changing code: and per-window runtime state. Keep this layer backend-neutral; it may depend on `editor/app/Rendering/Host` contracts, but not concrete render backends. - `editor/app/Platform/Win32`: native window, message dispatch, input, - lifecycle, chrome, HWND ownership, and native host adapter behavior. + lifecycle, chrome, HWND ownership, Win32 diagnostics, concrete native render + surface adapters, and native host adapter behavior. - `editor/app/Rendering/Host`: app rendering contracts consumed by app windowing and app content, including the render-runtime factory interface. - `editor/app/Rendering/D3D12`: concrete D3D12 window renderer, UI renderer, @@ -137,14 +138,15 @@ EditorWindowManager / coordinators -> EditorWindowInstance renders from snapshots and returns native frame commands ``` -The native peer contract for frame/runtime data is data-shaped. Native surface -creation crosses through `EditorNativeWindowRuntimeSurface`; per-frame native -facts cross through `EditorNativeWindowFrameSnapshot`; frame-side native -effects cross back through `EditorNativeWindowFrameCommands`. Do not add -one-off native-peer getters for normal frame data such as HWND, render size, -DPI, pending input, cursor point, workspace bounds, title-bar mode, or cursor -application. Extend the snapshot/command structs when the frame contract needs -new data. +The native peer contract for frame/runtime data is data-shaped. Native render +surface creation crosses through `EditorNativeWindowRuntimeSurface`, which +carries an abstract `Rendering::Host::EditorWindowRenderRuntimeSurface` rather +than a raw HWND/`void*`; per-frame native facts cross through +`EditorNativeWindowFrameSnapshot`; frame-side native effects cross back through +`EditorNativeWindowFrameCommands`. Do not add one-off native-peer getters for +normal frame data such as HWND, render size, DPI, pending input, cursor point, +workspace bounds, title-bar mode, or cursor application. Extend the +snapshot/command structs when the frame contract needs new data. ## Window Authority Model @@ -219,13 +221,19 @@ content controllers. Utility windows are descriptor driven through `EditorUtilityWindowDescriptor`, `EditorUtilityWindowRegistry`, and `CreateEditorUtilityWindowPanel(...)`. Register new utility windows there rather than hard-coding them in Win32 host -logic. +logic. Utility descriptors may express native host semantics such as +`EditorWindowNativeShellRole::ToolWindow`, but they must not include +`windows.h` or encode Win32 style constants such as `WS_EX_*` or `WS_*`. ## Modification Rules - First decide whether the change belongs to XCEditor framework, app semantics, app windowing, Win32 host, or rendering host. - Keep public framework headers free of `App::*`, Win32, and D3D12 host types. +- Keep `editor/include/XCEditor` and `editor/src` free of `windows.h`, HWND, + Win32 exception types such as `_EXCEPTION_POINTERS`, and DbgHelp/StackWalk + code. Win32 crash stack capture belongs under `editor/app/Platform/Win32`; + framework trace code only writes platform-neutral trace records. - Do not add `editor/app` as an include directory for `XCUIEditorLib`. App-only services must cross into framework code through framework-owned interfaces and app-side adapters. @@ -250,14 +258,19 @@ logic. - Use `editor/app/Platform/Win32` only for native host behavior and message integration. Win32 code may request frames through the host coordinator; it must not own the editor frame loop, expose `EditorContext`, or create - concrete render backends. + concrete render backends. It may create concrete Win32 render surface adapter + objects that implement `Rendering::Host::EditorWindowRenderRuntimeSurface`. - Do not let `editor/app/Windowing` include `Platform/Win32` headers. - Do not let `editor/app/Windowing` include `Rendering/D3D12` headers, `windows.h`, or HWND types. It should consume concrete rendering only through `Rendering::Host::EditorWindowRenderRuntime` and - `Rendering::Host::EditorWindowRenderRuntimeFactory`. + `Rendering::Host::EditorWindowRenderRuntimeFactory`, and it must pass native + render surfaces only as abstract `EditorWindowRenderRuntimeSurface` objects. - Do not let `editor/app/Platform/Win32` include `Rendering/D3D12` headers or implement render-runtime factory methods on the native host interfaces. +- Do not let app semantics such as `UtilityWindows` encode Win32 style bits. + Use semantic host policy fields, and map those semantics to `WS_*` constants + inside the Win32 host. - Do not spread D3D12 host types into app windowing content, coordinator, frame-transfer, runtime-controller, or public host-contract headers. - Do not let Win32 host code create workspace or utility content directly. @@ -267,7 +280,8 @@ logic. use `EditorNativeWindowRuntimeSurface`, `EditorNativeWindowFrameSnapshot`, `EditorNativeWindowFrameCommands`, and small metrics snapshots instead of exposing individual HWND, DPI, size, input, cursor, or title-bar getters to - app windowing. + app windowing. `EditorNativeWindowRuntimeSurface` must not expose a raw + native handle field. - Do not let Win32 host code call `EditorWindowRuntimeController` or directly drive editor runtime frames. Native messages may request immediate frames through `EditorWindowHostCoordinator`; app windowing owns the render/update @@ -320,15 +334,26 @@ HWND, DPI, size, input, cursor, workspace bounds, and title-bar mode. Win32 captures those native facts into `EditorNativeWindowRuntimeSurface` and `EditorNativeWindowFrameSnapshot`; app windowing renders from the snapshot and returns native side effects through `EditorNativeWindowFrameCommands`. +`EditorNativeWindowRuntimeSurface` carries an abstract render surface object, +not a raw HWND or erased `void*`. The renderer ownership cut has also been made: app windowing no longer owns the concrete D3D12 window render loop or Win32 surface setup. `EditorWindowRuntimeController` consumes a backend-neutral `Rendering::Host::EditorWindowRenderRuntime`, and `EditorWindowManager` receives a backend-neutral `Rendering::Host::EditorWindowRenderRuntimeFactory`. The -current D3D12 factory is composed in `Application`, not in the Win32 host. +current D3D12 factory is composed in `Application`, not in the Win32 host. The +current D3D12 runtime may recognize the concrete Win32 render surface adapter, +but app windowing must only traffic in the abstract rendering-host surface type. -The remaining promotion debt is the app runtime surface itself. +The platform-boundary cut has also been made: `XCUIEditorLib` trace code is +platform-neutral, and Win32 exception/DbgHelp stack capture lives under +`editor/app/Platform/Win32/Diagnostics`. Utility-window descriptors express +native shell intent with semantic host policy values rather than Win32 style +bits; the Win32 host maps those semantics to `WS_*` constants. + +The remaining promotion debt is the app runtime surface and host contract +vocabulary itself. `editor/app/Windowing` is still app-internal and may depend on app semantics such as `EditorContext`, `EditorShellRuntime`, utility window descriptors, and product-specific content. This app-internal dependency is a directory-level @@ -344,9 +369,11 @@ Do not take another library-boundary architecture cut just to keep carving this area. After the native-peer and native-frame-contract cuts, the next default improvement should be boundary guardrails, such as checks that keep `editor/app/Windowing` free of `Rendering/D3D12`, `Platform/Win32`, -`windows.h`, HWND types, and one-off native-peer frame getters, and keep -`editor/app/Platform/Win32` free of `Rendering/D3D12` and -`Windowing/Runtime/EditorWindowRuntimeController.h`. Consider another +`windows.h`, HWND types, raw native render handles, and one-off native-peer +frame getters; keep app semantics free of Win32 `WS_*` constants; keep +`editor/include/XCEditor` and `editor/src` free of Win32 diagnostics and +exception types; and keep `editor/app/Platform/Win32` free of `Rendering/D3D12` +and `Windowing/Runtime/EditorWindowRuntimeController.h`. Consider another structural source-directory cut only when there is concrete pressure, such as another native host, another render backend, or headless editor/window tests; do not satisfy that pressure by adding another production static library. @@ -401,6 +428,8 @@ Start with these files for editor/windowing work: - `editor/app/Windowing/EditorWindowManager.*` - `editor/app/Rendering/Host/EditorWindowRenderRuntime.h` - `editor/app/Rendering/D3D12/D3D12EditorWindowRenderRuntime.*` +- `editor/app/Platform/Win32/Diagnostics/Win32CrashTrace.*` +- `editor/app/Platform/Win32/Windowing/Win32EditorWindowRenderRuntimeSurface.h` - `editor/app/Platform/Win32/Windowing/EditorWindow.*` - `editor/app/Platform/Win32/Windowing/EditorWindowHostRuntime.*` - `editor/app/Platform/Win32/Windowing/EditorWindowMessageDispatcher.*` @@ -434,6 +463,8 @@ Start with these files for editor/windowing work: `EditorNativeWindowRuntimeSurface` and `EditorNativeWindowFrameSnapshot` instead of calling granular native-peer getters for frame/runtime data, and Win32 applies frame side effects through `EditorNativeWindowFrameCommands`. + Runtime surface creation carries an abstract + `Rendering::Host::EditorWindowRenderRuntimeSurface`, not a raw HWND/`void*`. - The concrete renderer cut is sealed: app windowing consumes `Rendering::Host::EditorWindowRenderRuntime` and `Rendering::Host::EditorWindowRenderRuntimeFactory`, while the current D3D12 @@ -441,6 +472,11 @@ Start with these files for editor/windowing work: factory is composed in `Application`; `editor/app/Windowing` should stay free of `Rendering/D3D12`, `windows.h`, and HWND types, and `editor/app/Platform/Win32` should stay free of `Rendering/D3D12`. +- The Win32 platform-boundary cut is sealed: framework runtime trace code no + longer includes Win32/DbgHelp or exposes `_EXCEPTION_POINTERS`; Win32 crash + stack capture lives under `editor/app/Platform/Win32/Diagnostics`; utility + window descriptors use semantic native host policies instead of `WS_*` + constants. - The production target shape is sealed: `XCUIEditorLib` is the only editor static library, and all app-side source directories compile into `XCUIEditorApp`. diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index 1dc7a01f..70fed00d 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -281,6 +281,7 @@ if(XCENGINE_BUILD_XCUI_EDITOR_APP) ) set(XCUI_EDITOR_APP_PLATFORM_SOURCES + app/Platform/Win32/Diagnostics/Win32CrashTrace.cpp app/Platform/Win32/Windowing/EditorWindow.cpp app/Platform/Win32/Windowing/EditorFloatingWindowPlacement.cpp app/Platform/Win32/Windowing/EditorWindowSession.cpp @@ -332,6 +333,7 @@ if(XCENGINE_BUILD_XCUI_EDITOR_APP) XCEngineRenderingEditorSupport d3d12.lib d3dcompiler.lib + dbghelp.lib dwrite.lib dxgi.lib windowscodecs.lib diff --git a/editor/app/Bootstrap/Application.cpp b/editor/app/Bootstrap/Application.cpp index 133ee587..bc79ca35 100644 --- a/editor/app/Bootstrap/Application.cpp +++ b/editor/app/Bootstrap/Application.cpp @@ -3,6 +3,7 @@ #include "System/SystemInteractionService.h" #include "Composition/EditorContext.h" #include "Windowing/EditorWindowManager.h" +#include "Platform/Win32/Diagnostics/Win32CrashTrace.h" #include "Platform/Win32/System/Win32SystemInteractionHost.h" #include "Platform/Win32/Windowing/EditorWindow.h" #include "Platform/Win32/Windowing/EditorWindowHostConfig.h" @@ -263,16 +264,7 @@ std::filesystem::path Application::ResolveRepoRootPath() { } LONG WINAPI Application::HandleUnhandledException(EXCEPTION_POINTERS* exceptionInfo) { - if (exceptionInfo != nullptr && - exceptionInfo->ExceptionRecord != nullptr) { - AppendUIEditorCrashTrace( - exceptionInfo->ExceptionRecord->ExceptionCode, - exceptionInfo->ExceptionRecord->ExceptionAddress, - exceptionInfo); - } else { - AppendUIEditorCrashTrace(0u, nullptr); - } - + App::AppendWin32UnhandledExceptionCrashTrace(exceptionInfo); return EXCEPTION_EXECUTE_HANDLER; } diff --git a/editor/app/Platform/Win32/Diagnostics/Win32CrashTrace.cpp b/editor/app/Platform/Win32/Diagnostics/Win32CrashTrace.cpp new file mode 100644 index 00000000..4e57234b --- /dev/null +++ b/editor/app/Platform/Win32/Diagnostics/Win32CrashTrace.cpp @@ -0,0 +1,186 @@ +#include "Platform/Win32/Diagnostics/Win32CrashTrace.h" + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#pragma comment(lib, "Dbghelp.lib") + +namespace XCEngine::UI::Editor::App { + +namespace { + +bool g_symbolHandlerInitialized = false; + +std::string NarrowWideString(std::wstring_view text) { + if (text.empty()) { + return {}; + } + + const int sizeNeeded = WideCharToMultiByte( + CP_UTF8, + 0, + text.data(), + static_cast(text.size()), + nullptr, + 0, + nullptr, + nullptr); + if (sizeNeeded <= 0) { + return {}; + } + + std::string result(static_cast(sizeNeeded), '\0'); + WideCharToMultiByte( + CP_UTF8, + 0, + text.data(), + static_cast(text.size()), + result.data(), + sizeNeeded, + nullptr, + nullptr); + return result; +} + +std::string ResolveModulePathFromAddress(DWORD64 address) { + if (address == 0u) { + return {}; + } + + HMODULE moduleHandle = nullptr; + if (!GetModuleHandleExW( + GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | + GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + reinterpret_cast(address), + &moduleHandle) || + moduleHandle == nullptr) { + return {}; + } + + wchar_t modulePath[MAX_PATH] = {}; + const DWORD length = GetModuleFileNameW(moduleHandle, modulePath, MAX_PATH); + if (length == 0u) { + return {}; + } + + return NarrowWideString(std::wstring_view(modulePath, length)); +} + +void EnsureSymbolHandlerInitialized(HANDLE process) { + if (g_symbolHandlerInitialized) { + return; + } + + SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES); + g_symbolHandlerInitialized = SymInitialize(process, nullptr, TRUE) == TRUE; +} + +std::vector CaptureExceptionStack( + const EXCEPTION_POINTERS* exceptionPointers) { + std::vector frames = {}; + if (exceptionPointers == nullptr || exceptionPointers->ContextRecord == nullptr) { + return frames; + } + + HANDLE process = GetCurrentProcess(); + HANDLE thread = GetCurrentThread(); + EnsureSymbolHandlerInitialized(process); + + CONTEXT context = *exceptionPointers->ContextRecord; + STACKFRAME64 stackFrame = {}; + DWORD machineType = 0u; +#if defined(_M_X64) + machineType = IMAGE_FILE_MACHINE_AMD64; + stackFrame.AddrPC.Offset = context.Rip; + stackFrame.AddrFrame.Offset = context.Rbp; + stackFrame.AddrStack.Offset = context.Rsp; +#elif defined(_M_IX86) + machineType = IMAGE_FILE_MACHINE_I386; + stackFrame.AddrPC.Offset = context.Eip; + stackFrame.AddrFrame.Offset = context.Ebp; + stackFrame.AddrStack.Offset = context.Esp; +#else + return frames; +#endif + stackFrame.AddrPC.Mode = AddrModeFlat; + stackFrame.AddrFrame.Mode = AddrModeFlat; + stackFrame.AddrStack.Mode = AddrModeFlat; + + constexpr std::size_t kMaxFrames = 32u; + frames.reserve(kMaxFrames); + for (std::size_t frameIndex = 0u; frameIndex < kMaxFrames; ++frameIndex) { + const BOOL advanced = StackWalk64( + machineType, + process, + thread, + &stackFrame, + &context, + nullptr, + SymFunctionTableAccess64, + SymGetModuleBase64, + nullptr); + if (advanced == FALSE || stackFrame.AddrPC.Offset == 0u) { + break; + } + + UIEditorCrashTraceFrame frame = {}; + frame.address = static_cast(stackFrame.AddrPC.Offset); + frame.moduleName = ResolveModulePathFromAddress(stackFrame.AddrPC.Offset); + + std::array symbolBuffer = {}; + auto* symbol = reinterpret_cast(symbolBuffer.data()); + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + symbol->MaxNameLen = MAX_SYM_NAME; + DWORD64 displacement = 0u; + if (g_symbolHandlerInitialized && + SymFromAddr(process, stackFrame.AddrPC.Offset, &displacement, symbol) == TRUE) { + frame.displacement = static_cast(displacement); + frame.symbolName = symbol->Name; + } + + IMAGEHLP_LINE64 line = {}; + line.SizeOfStruct = sizeof(line); + DWORD lineDisplacement = 0u; + if (g_symbolHandlerInitialized && + SymGetLineFromAddr64( + process, + stackFrame.AddrPC.Offset, + &lineDisplacement, + &line) == TRUE) { + frame.line = line.LineNumber; + frame.lineDisplacement = lineDisplacement; + if (line.FileName != nullptr) { + frame.sourceFile = line.FileName; + } + } + + frames.push_back(std::move(frame)); + } + + return frames; +} + +} // namespace + +void AppendWin32UnhandledExceptionCrashTrace(EXCEPTION_POINTERS* exceptionInfo) { + UIEditorCrashTrace trace = {}; + if (exceptionInfo != nullptr && exceptionInfo->ExceptionRecord != nullptr) { + trace.exceptionCode = exceptionInfo->ExceptionRecord->ExceptionCode; + trace.exceptionAddress = exceptionInfo->ExceptionRecord->ExceptionAddress; + trace.exceptionModule = ResolveModulePathFromAddress( + reinterpret_cast(trace.exceptionAddress)); + } + trace.threadId = GetCurrentThreadId(); + trace.stackFrames = CaptureExceptionStack(exceptionInfo); + AppendUIEditorCrashTrace(trace); +} + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Platform/Win32/Diagnostics/Win32CrashTrace.h b/editor/app/Platform/Win32/Diagnostics/Win32CrashTrace.h new file mode 100644 index 00000000..ce7bfacd --- /dev/null +++ b/editor/app/Platform/Win32/Diagnostics/Win32CrashTrace.h @@ -0,0 +1,13 @@ +#pragma once + +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#include + +namespace XCEngine::UI::Editor::App { + +void AppendWin32UnhandledExceptionCrashTrace(EXCEPTION_POINTERS* exceptionInfo); + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Platform/Win32/Windowing/EditorWindow.cpp b/editor/app/Platform/Win32/Windowing/EditorWindow.cpp index 3255d71c..0d83ee01 100644 --- a/editor/app/Platform/Win32/Windowing/EditorWindow.cpp +++ b/editor/app/Platform/Win32/Windowing/EditorWindow.cpp @@ -3,6 +3,7 @@ #include "Platform/Win32/Chrome/EditorWindowChromeController.h" #include "Platform/Win32/Windowing/EditorWindowSession.h" #include "Platform/Win32/Windowing/EditorWindowSupport.h" +#include "Platform/Win32/Windowing/Win32EditorWindowRenderRuntimeSurface.h" #include "Platform/Win32/Runtime/EditorWindowInputController.h" #include "Windowing/Content/EditorWindowContentController.h" #include @@ -11,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -179,7 +181,8 @@ bool EditorWindow::CaptureRuntimeSurface( clientHeight = 1u; } - outSurface.nativeWindowHandle = hwnd; + outSurface.renderSurface = + std::make_shared(hwnd); outSurface.widthPixels = clientWidth; outSurface.heightPixels = clientHeight; outSurface.dpiScale = GetDpiScale(); diff --git a/editor/app/Platform/Win32/Windowing/EditorWindowHostRuntime.cpp b/editor/app/Platform/Win32/Windowing/EditorWindowHostRuntime.cpp index 400041a8..dbfb6662 100644 --- a/editor/app/Platform/Win32/Windowing/EditorWindowHostRuntime.cpp +++ b/editor/app/Platform/Win32/Windowing/EditorWindowHostRuntime.cpp @@ -49,6 +49,16 @@ std::string DescribeHostWindows( return stream.str(); } +DWORD ResolveExtendedWindowStyle(const EditorWindowNativeHostPolicy& policy) { + switch (policy.shellRole) { + case EditorWindowNativeShellRole::ToolWindow: + return WS_EX_TOOLWINDOW; + case EditorWindowNativeShellRole::AppWindow: + default: + return WS_EX_APPWINDOW; + } +} + } // namespace EditorWindowHostRuntime::EditorWindowHostRuntime( @@ -91,13 +101,8 @@ bool EditorWindowHostRuntime::CreateHostWindow( }; m_pendingCreateWindow = rawWindow; - const DWORD windowStyle = params.nativeStylePolicy.useHostWindowStyle - ? m_hostConfig.windowStyle - : static_cast(params.nativeStylePolicy.windowStyle); - const DWORD extendedWindowStyle = - params.nativeStylePolicy.extendedWindowStyle != 0u - ? static_cast(params.nativeStylePolicy.extendedWindowStyle) - : WS_EX_APPWINDOW; + const DWORD windowStyle = m_hostConfig.windowStyle; + const DWORD extendedWindowStyle = ResolveExtendedWindowStyle(params.nativeHostPolicy); const int initialX = params.initialX == kEditorWindowDefaultPosition ? CW_USEDEFAULT : params.initialX; diff --git a/editor/app/Platform/Win32/Windowing/Win32EditorWindowRenderRuntimeSurface.h b/editor/app/Platform/Win32/Windowing/Win32EditorWindowRenderRuntimeSurface.h new file mode 100644 index 00000000..7de0bada --- /dev/null +++ b/editor/app/Platform/Win32/Windowing/Win32EditorWindowRenderRuntimeSurface.h @@ -0,0 +1,33 @@ +#pragma once + +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#include "Rendering/Host/EditorWindowRenderRuntime.h" + +#include + +namespace XCEngine::UI::Editor::App { + +class Win32EditorWindowRenderRuntimeSurface final + : public ::XCEngine::UI::Editor::Rendering::Host::EditorWindowRenderRuntimeSurface { +public: + explicit Win32EditorWindowRenderRuntimeSurface(HWND hwnd) + : m_hwnd(hwnd) {} + + ::XCEngine::UI::Editor::Rendering::Host::EditorWindowRenderRuntimeSurfaceKind + GetKind() const override { + return ::XCEngine::UI::Editor::Rendering::Host:: + EditorWindowRenderRuntimeSurfaceKind::Win32Window; + } + + HWND GetHwnd() const { + return m_hwnd; + } + +private: + HWND m_hwnd = nullptr; +}; + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Rendering/D3D12/D3D12EditorWindowRenderRuntime.cpp b/editor/app/Rendering/D3D12/D3D12EditorWindowRenderRuntime.cpp index e8fa73a6..9808c967 100644 --- a/editor/app/Rendering/D3D12/D3D12EditorWindowRenderRuntime.cpp +++ b/editor/app/Rendering/D3D12/D3D12EditorWindowRenderRuntime.cpp @@ -1,5 +1,7 @@ #include "Rendering/D3D12/D3D12EditorWindowRenderRuntime.h" +#include "Platform/Win32/Windowing/Win32EditorWindowRenderRuntimeSurface.h" + #include #include @@ -11,9 +13,9 @@ namespace { using Rendering::Host::EditorWindowRenderRuntimeFrameContext; using Rendering::Host::EditorWindowRenderRuntimeInitializeResult; +using Rendering::Host::EditorWindowRenderRuntimeInitializeParams; using Rendering::Host::EditorWindowRenderRuntimePresentResult; using Rendering::Host::EditorWindowRenderRuntimeResizeResult; -using Rendering::Host::EditorWindowRenderRuntimeSurface; } // namespace @@ -51,17 +53,22 @@ Rendering::Host::ViewportRenderHost& D3D12EditorWindowRenderRuntime::GetViewport } EditorWindowRenderRuntimeInitializeResult D3D12EditorWindowRenderRuntime::Initialize( - const EditorWindowRenderRuntimeSurface& surface) { + const EditorWindowRenderRuntimeInitializeParams& params) { EditorWindowRenderRuntimeInitializeResult result = {}; - HWND hwnd = static_cast(surface.nativeWindowHandle); + const auto* surface = params.surface != nullptr + ? dynamic_cast< + const ::XCEngine::UI::Editor::App::Win32EditorWindowRenderRuntimeSurface*>( + params.surface.get()) + : nullptr; + HWND hwnd = surface != nullptr ? surface->GetHwnd() : nullptr; if (hwnd == nullptr) { result.errorMessage = "window initialize skipped: hwnd is null"; return result; } - const int clientWidth = (std::max)(static_cast(surface.widthPixels), 1); - const int clientHeight = (std::max)(static_cast(surface.heightPixels), 1); + const int clientWidth = (std::max)(static_cast(params.widthPixels), 1); + const int clientHeight = (std::max)(static_cast(params.heightPixels), 1); if (!m_windowRenderer.Initialize(hwnd, clientWidth, clientHeight)) { result.errorMessage = "d3d12 window renderer initialization failed"; return result; diff --git a/editor/app/Rendering/D3D12/D3D12EditorWindowRenderRuntime.h b/editor/app/Rendering/D3D12/D3D12EditorWindowRenderRuntime.h index d55e9768..6e2add14 100644 --- a/editor/app/Rendering/D3D12/D3D12EditorWindowRenderRuntime.h +++ b/editor/app/Rendering/D3D12/D3D12EditorWindowRenderRuntime.h @@ -34,7 +34,7 @@ public: Rendering::Host::ViewportRenderHost& GetViewportRenderHost() override; Rendering::Host::EditorWindowRenderRuntimeInitializeResult Initialize( - const Rendering::Host::EditorWindowRenderRuntimeSurface& surface) override; + const Rendering::Host::EditorWindowRenderRuntimeInitializeParams& params) override; void WaitForGpuIdle() override; void Shutdown() override; Rendering::Host::EditorWindowRenderRuntimeResizeResult ApplyResize( diff --git a/editor/app/Rendering/Host/EditorWindowRenderRuntime.h b/editor/app/Rendering/Host/EditorWindowRenderRuntime.h index 3ebf1461..ed061f11 100644 --- a/editor/app/Rendering/Host/EditorWindowRenderRuntime.h +++ b/editor/app/Rendering/Host/EditorWindowRenderRuntime.h @@ -14,10 +14,26 @@ namespace XCEngine::UI::Editor::Rendering::Host { -struct EditorWindowRenderRuntimeSurface { - void* nativeWindowHandle = nullptr; +enum class EditorWindowRenderRuntimeSurfaceKind : std::uint8_t { + Unknown = 0, + Win32Window, +}; + +class EditorWindowRenderRuntimeSurface { +public: + virtual ~EditorWindowRenderRuntimeSurface() = default; + + virtual EditorWindowRenderRuntimeSurfaceKind GetKind() const = 0; +}; + +struct EditorWindowRenderRuntimeInitializeParams { + std::shared_ptr surface = {}; std::uint32_t widthPixels = 0u; std::uint32_t heightPixels = 0u; + + bool IsValid() const { + return surface != nullptr && widthPixels > 0u && heightPixels > 0u; + } }; struct EditorWindowRenderRuntimeInitializeResult { @@ -57,7 +73,7 @@ public: virtual ViewportRenderHost& GetViewportRenderHost() = 0; virtual EditorWindowRenderRuntimeInitializeResult Initialize( - const EditorWindowRenderRuntimeSurface& surface) = 0; + const EditorWindowRenderRuntimeInitializeParams& params) = 0; virtual void WaitForGpuIdle() = 0; virtual void Shutdown() = 0; virtual EditorWindowRenderRuntimeResizeResult ApplyResize( diff --git a/editor/app/UtilityWindows/EditorUtilityWindowRegistry.cpp b/editor/app/UtilityWindows/EditorUtilityWindowRegistry.cpp index f42288c9..cb27ca9d 100644 --- a/editor/app/UtilityWindows/EditorUtilityWindowRegistry.cpp +++ b/editor/app/UtilityWindows/EditorUtilityWindowRegistry.cpp @@ -3,12 +3,6 @@ #include "Features/ColorPicker/ColorPickerPanel.h" #include "Features/Inspector/AddComponentPanel.h" -#ifndef NOMINMAX -#define NOMINMAX -#endif - -#include - namespace XCEngine::UI::Editor::App { namespace { @@ -22,10 +16,8 @@ constexpr EditorWindowChromePolicy kUtilityWindowChromePolicy = { .topmostByDefault = true, }; -constexpr EditorWindowNativeStylePolicy kUtilityWindowNativeStylePolicy = { - .extendedWindowStyle = WS_EX_TOOLWINDOW, - .windowStyle = 0, - .useHostWindowStyle = true, +constexpr EditorWindowNativeHostPolicy kUtilityWindowNativeHostPolicy = { + .shellRole = EditorWindowNativeShellRole::ToolWindow, }; constexpr EditorUtilityWindowDescriptor kColorPickerUtilityWindowDescriptor = { @@ -33,7 +25,7 @@ constexpr EditorUtilityWindowDescriptor kColorPickerUtilityWindowDescriptor = { .windowId = "utility.color-picker", .title = L"Color Picker", .chromePolicy = kUtilityWindowChromePolicy, - .nativeStylePolicy = kUtilityWindowNativeStylePolicy, + .nativeHostPolicy = kUtilityWindowNativeHostPolicy, .preferredOuterSize = UISize(400.0f, 600.0f), .minimumOuterSize = UISize(360.0f, 560.0f), }; @@ -43,7 +35,7 @@ constexpr EditorUtilityWindowDescriptor kAddComponentUtilityWindowDescriptor = { .windowId = "utility.add-component", .title = L"Add Component", .chromePolicy = kUtilityWindowChromePolicy, - .nativeStylePolicy = kUtilityWindowNativeStylePolicy, + .nativeHostPolicy = kUtilityWindowNativeHostPolicy, .preferredOuterSize = UISize(352.0f, 500.0f), .minimumOuterSize = UISize(320.0f, 460.0f), }; diff --git a/editor/app/UtilityWindows/EditorUtilityWindowRegistry.h b/editor/app/UtilityWindows/EditorUtilityWindowRegistry.h index eda271e8..89a747d5 100644 --- a/editor/app/UtilityWindows/EditorUtilityWindowRegistry.h +++ b/editor/app/UtilityWindows/EditorUtilityWindowRegistry.h @@ -22,7 +22,7 @@ struct EditorUtilityWindowDescriptor { EditorUtilityWindowReusePolicy reusePolicy = EditorUtilityWindowReusePolicy::SingleInstance; EditorWindowChromePolicy chromePolicy = {}; - EditorWindowNativeStylePolicy nativeStylePolicy = {}; + EditorWindowNativeHostPolicy nativeHostPolicy = {}; ::XCEngine::UI::UISize preferredOuterSize = {}; ::XCEngine::UI::UISize minimumOuterSize = {}; }; diff --git a/editor/app/Windowing/Coordinator/EditorUtilityWindowCoordinator.cpp b/editor/app/Windowing/Coordinator/EditorUtilityWindowCoordinator.cpp index 2ac91169..b4eb0662 100644 --- a/editor/app/Windowing/Coordinator/EditorUtilityWindowCoordinator.cpp +++ b/editor/app/Windowing/Coordinator/EditorUtilityWindowCoordinator.cpp @@ -113,7 +113,7 @@ bool EditorUtilityWindowCoordinator::TryProcessOpenUtilityWindowRequest( createParams.title = descriptor->title; createParams.category = EditorWindowCategory::Utility; createParams.chromePolicy = descriptor->chromePolicy; - createParams.nativeStylePolicy = descriptor->nativeStylePolicy; + createParams.nativeHostPolicy = descriptor->nativeHostPolicy; createParams.primary = false; createParams.initialWidth = ResolveOuterDimension( descriptor->preferredOuterSize.width, diff --git a/editor/app/Windowing/EditorWindowInstance.cpp b/editor/app/Windowing/EditorWindowInstance.cpp index 8b1d59b9..4170f8c2 100644 --- a/editor/app/Windowing/EditorWindowInstance.cpp +++ b/editor/app/Windowing/EditorWindowInstance.cpp @@ -307,8 +307,8 @@ bool EditorWindowInstance::InitializeRuntime( MarkInitializing(); const bool initialized = m_runtime->Initialize( - Rendering::Host::EditorWindowRenderRuntimeSurface{ - .nativeWindowHandle = runtimeSurface.nativeWindowHandle, + Rendering::Host::EditorWindowRenderRuntimeInitializeParams{ + .surface = runtimeSurface.renderSurface, .widthPixels = runtimeSurface.widthPixels, .heightPixels = runtimeSurface.heightPixels, }, diff --git a/editor/app/Windowing/Host/EditorWindowHostInterfaces.h b/editor/app/Windowing/Host/EditorWindowHostInterfaces.h index 9d5976f3..48f404de 100644 --- a/editor/app/Windowing/Host/EditorWindowHostInterfaces.h +++ b/editor/app/Windowing/Host/EditorWindowHostInterfaces.h @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -28,6 +29,10 @@ namespace XCEngine::UI::Editor { struct UIEditorDockHostTabDropTarget; +namespace Rendering::Host { +class EditorWindowRenderRuntimeSurface; +} + } // namespace XCEngine::UI::Editor namespace XCEngine::UI::Editor::App { @@ -44,13 +49,13 @@ struct EditorHostWindowRuntimeInitializationParams { }; struct EditorNativeWindowRuntimeSurface { - void* nativeWindowHandle = nullptr; + std::shared_ptr renderSurface = {}; std::uint32_t widthPixels = 0u; std::uint32_t heightPixels = 0u; float dpiScale = 1.0f; bool IsValid() const { - return nativeWindowHandle != nullptr && widthPixels > 0u && heightPixels > 0u; + return renderSurface != nullptr && widthPixels > 0u && heightPixels > 0u; } }; diff --git a/editor/app/Windowing/Host/EditorWindowHostTypes.h b/editor/app/Windowing/Host/EditorWindowHostTypes.h index 4fa65b54..776f7e1f 100644 --- a/editor/app/Windowing/Host/EditorWindowHostTypes.h +++ b/editor/app/Windowing/Host/EditorWindowHostTypes.h @@ -21,7 +21,7 @@ struct EditorWindowCreateParams { int showCommand = kEditorWindowDefaultShowCommand; EditorWindowCategory category = EditorWindowCategory::Workspace; EditorWindowChromePolicy chromePolicy = {}; - EditorWindowNativeStylePolicy nativeStylePolicy = {}; + EditorWindowNativeHostPolicy nativeHostPolicy = {}; bool primary = false; bool autoCaptureOnStartup = false; }; diff --git a/editor/app/Windowing/Host/EditorWindowTypes.h b/editor/app/Windowing/Host/EditorWindowTypes.h index 2fa396b3..5e5d90be 100644 --- a/editor/app/Windowing/Host/EditorWindowTypes.h +++ b/editor/app/Windowing/Host/EditorWindowTypes.h @@ -19,6 +19,11 @@ enum class EditorWindowCategory : std::uint8_t { Utility, }; +enum class EditorWindowNativeShellRole : std::uint8_t { + AppWindow = 0, + ToolWindow, +}; + inline std::string_view GetEditorWindowCategoryName( EditorWindowCategory category) { switch (category) { @@ -38,10 +43,8 @@ struct EditorWindowChromePolicy { bool topmostByDefault = false; }; -struct EditorWindowNativeStylePolicy { - std::uint32_t extendedWindowStyle = 0u; - std::uint32_t windowStyle = 0u; - bool useHostWindowStyle = true; +struct EditorWindowNativeHostPolicy { + EditorWindowNativeShellRole shellRole = EditorWindowNativeShellRole::AppWindow; }; inline std::string_view GetEditorWindowLifecycleStateName( diff --git a/editor/app/Windowing/Runtime/EditorWindowRuntimeController.cpp b/editor/app/Windowing/Runtime/EditorWindowRuntimeController.cpp index 5d869b87..bb1d5f77 100644 --- a/editor/app/Windowing/Runtime/EditorWindowRuntimeController.cpp +++ b/editor/app/Windowing/Runtime/EditorWindowRuntimeController.cpp @@ -127,7 +127,7 @@ const ::XCEngine::UI::UITextureHandle& EditorWindowRuntimeController::GetTitleBa } bool EditorWindowRuntimeController::Initialize( - const Rendering::Host::EditorWindowRenderRuntimeSurface& renderSurface, + const Rendering::Host::EditorWindowRenderRuntimeInitializeParams& renderParams, const std::filesystem::path& repoRoot, const std::filesystem::path& captureRoot, bool autoCaptureOnStartup) { @@ -137,7 +137,7 @@ bool EditorWindowRuntimeController::Initialize( } const Rendering::Host::EditorWindowRenderRuntimeInitializeResult initializeResult = - m_renderRuntime->Initialize(renderSurface); + m_renderRuntime->Initialize(renderParams); if (!initializeResult.success) { LogRuntimeTrace("app", initializeResult.errorMessage.empty() ? "window render runtime initialization failed" diff --git a/editor/app/Windowing/Runtime/EditorWindowRuntimeController.h b/editor/app/Windowing/Runtime/EditorWindowRuntimeController.h index 050cfa0b..31183f28 100644 --- a/editor/app/Windowing/Runtime/EditorWindowRuntimeController.h +++ b/editor/app/Windowing/Runtime/EditorWindowRuntimeController.h @@ -62,7 +62,7 @@ public: const ::XCEngine::UI::UITextureHandle& GetTitleBarLogoIcon() const; bool Initialize( - const Rendering::Host::EditorWindowRenderRuntimeSurface& renderSurface, + const Rendering::Host::EditorWindowRenderRuntimeInitializeParams& renderParams, const std::filesystem::path& repoRoot, const std::filesystem::path& captureRoot, bool autoCaptureOnStartup); diff --git a/editor/include/XCEditor/Foundation/UIEditorRuntimeTrace.h b/editor/include/XCEditor/Foundation/UIEditorRuntimeTrace.h index 56c6e15b..373fa28c 100644 --- a/editor/include/XCEditor/Foundation/UIEditorRuntimeTrace.h +++ b/editor/include/XCEditor/Foundation/UIEditorRuntimeTrace.h @@ -2,10 +2,30 @@ #include #include +#include #include +#include namespace XCEngine::UI::Editor { +struct UIEditorCrashTraceFrame { + std::uintptr_t address = 0u; + std::uintptr_t displacement = 0u; + std::uint32_t line = 0u; + std::uint32_t lineDisplacement = 0u; + std::string moduleName = {}; + std::string symbolName = {}; + std::string sourceFile = {}; +}; + +struct UIEditorCrashTrace { + std::uint32_t exceptionCode = 0u; + const void* exceptionAddress = nullptr; + std::uint32_t threadId = 0u; + std::string exceptionModule = {}; + std::vector stackFrames = {}; +}; + void InitializeUIEditorRuntimeTrace(const std::filesystem::path& logRoot); void ShutdownUIEditorRuntimeTrace(); @@ -16,10 +36,7 @@ void AppendUIEditorRuntimeTrace( void AppendUIEditorCrashTrace( std::uint32_t exceptionCode, const void* exceptionAddress); -void AppendUIEditorCrashTrace( - std::uint32_t exceptionCode, - const void* exceptionAddress, - const struct _EXCEPTION_POINTERS* exceptionPointers); +void AppendUIEditorCrashTrace(const UIEditorCrashTrace& trace); std::filesystem::path GetUIEditorRuntimeTracePath(); std::filesystem::path GetUIEditorCrashTracePath(); diff --git a/editor/src/Foundation/UIEditorRuntimeTrace.cpp b/editor/src/Foundation/UIEditorRuntimeTrace.cpp index 66d21d6b..0bacadd1 100644 --- a/editor/src/Foundation/UIEditorRuntimeTrace.cpp +++ b/editor/src/Foundation/UIEditorRuntimeTrace.cpp @@ -1,9 +1,7 @@ #include -#include -#include - #include +#include #include #include #include @@ -12,8 +10,6 @@ #include #include -#pragma comment(lib, "Dbghelp.lib") - namespace XCEngine::UI::Editor { namespace { @@ -25,17 +21,6 @@ std::filesystem::path g_crashTracePath = {}; std::ofstream g_runtimeTraceStream = {}; std::ofstream g_crashTraceStream = {}; bool g_traceInitialized = false; -bool g_symbolHandlerInitialized = false; - -struct CrashStackFrame { - DWORD64 address = 0u; - DWORD64 displacement = 0u; - DWORD line = 0u; - DWORD lineDisplacement = 0u; - std::string moduleName = {}; - std::string symbolName = {}; - std::string sourceFile = {}; -}; std::string BuildTimestampString() { const auto now = std::chrono::system_clock::now(); @@ -74,156 +59,23 @@ void AppendTraceLine( << '\n'; } -std::string NarrowWideString(std::wstring_view text) { - if (text.empty()) { - return {}; - } - - const int sizeNeeded = WideCharToMultiByte( - CP_UTF8, - 0, - text.data(), - static_cast(text.size()), - nullptr, - 0, - nullptr, - nullptr); - if (sizeNeeded <= 0) { - return {}; - } - - std::string result(static_cast(sizeNeeded), '\0'); - WideCharToMultiByte( - CP_UTF8, - 0, - text.data(), - static_cast(text.size()), - result.data(), - sizeNeeded, - nullptr, - nullptr); - return result; -} - std::string FormatPointer(const void* address) { char buffer[32] = {}; std::snprintf(buffer, sizeof(buffer), "%p", address); return buffer; } -std::string ResolveModulePathFromAddress(DWORD64 address) { - if (address == 0u) { - return {}; - } - - HMODULE moduleHandle = nullptr; - if (!GetModuleHandleExW( - GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | - GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, - reinterpret_cast(address), - &moduleHandle) || - moduleHandle == nullptr) { - return {}; - } - - wchar_t modulePath[MAX_PATH] = {}; - const DWORD length = GetModuleFileNameW(moduleHandle, modulePath, MAX_PATH); - if (length == 0u) { - return {}; - } - - return NarrowWideString(std::wstring_view(modulePath, length)); -} - -void EnsureSymbolHandlerInitialized(HANDLE process) { - if (g_symbolHandlerInitialized) { - return; - } - - SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES); - g_symbolHandlerInitialized = SymInitialize(process, nullptr, TRUE) == TRUE; -} - -std::vector CaptureExceptionStack(const _EXCEPTION_POINTERS* exceptionPointers) { - std::vector frames = {}; - if (exceptionPointers == nullptr || exceptionPointers->ContextRecord == nullptr) { - return frames; - } - - HANDLE process = GetCurrentProcess(); - HANDLE thread = GetCurrentThread(); - EnsureSymbolHandlerInitialized(process); - - CONTEXT context = *exceptionPointers->ContextRecord; - STACKFRAME64 stackFrame = {}; - DWORD machineType = 0u; -#if defined(_M_X64) - machineType = IMAGE_FILE_MACHINE_AMD64; - stackFrame.AddrPC.Offset = context.Rip; - stackFrame.AddrFrame.Offset = context.Rbp; - stackFrame.AddrStack.Offset = context.Rsp; -#elif defined(_M_IX86) - machineType = IMAGE_FILE_MACHINE_I386; - stackFrame.AddrPC.Offset = context.Eip; - stackFrame.AddrFrame.Offset = context.Ebp; - stackFrame.AddrStack.Offset = context.Esp; -#else - return frames; -#endif - stackFrame.AddrPC.Mode = AddrModeFlat; - stackFrame.AddrFrame.Mode = AddrModeFlat; - stackFrame.AddrStack.Mode = AddrModeFlat; - - constexpr std::size_t kMaxFrames = 32u; - frames.reserve(kMaxFrames); - for (std::size_t frameIndex = 0u; frameIndex < kMaxFrames; ++frameIndex) { - const BOOL advanced = StackWalk64( - machineType, - process, - thread, - &stackFrame, - &context, - nullptr, - SymFunctionTableAccess64, - SymGetModuleBase64, - nullptr); - if (advanced == FALSE || stackFrame.AddrPC.Offset == 0u) { - break; - } - - CrashStackFrame frame = {}; - frame.address = stackFrame.AddrPC.Offset; - frame.moduleName = ResolveModulePathFromAddress(frame.address); - - std::byte symbolBuffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME] = {}; - SYMBOL_INFO* symbol = reinterpret_cast(symbolBuffer); - symbol->SizeOfStruct = sizeof(SYMBOL_INFO); - symbol->MaxNameLen = MAX_SYM_NAME; - if (g_symbolHandlerInitialized && - SymFromAddr(process, frame.address, &frame.displacement, symbol) == TRUE) { - frame.symbolName = symbol->Name; - } - - IMAGEHLP_LINE64 line = {}; - line.SizeOfStruct = sizeof(line); - if (g_symbolHandlerInitialized && - SymGetLineFromAddr64(process, frame.address, &frame.lineDisplacement, &line) == TRUE) { - frame.line = line.LineNumber; - if (line.FileName != nullptr) { - frame.sourceFile = line.FileName; - } - } - - frames.push_back(std::move(frame)); - } - - return frames; -} - -std::string FormatCrashStackFrame(const CrashStackFrame& frame, std::size_t index) { +std::string FormatAddress(std::uintptr_t address) { std::ostringstream stream = {}; - stream << "stack[" << index << "] pc=0x" - << std::hex << std::uppercase << frame.address << std::dec; + stream << "0x" << std::hex << std::uppercase << address << std::dec; + return stream.str(); +} + +std::string FormatCrashStackFrame( + const UIEditorCrashTraceFrame& frame, + std::size_t index) { + std::ostringstream stream = {}; + stream << "stack[" << index << "] pc=" << FormatAddress(frame.address); if (!frame.moduleName.empty()) { stream << " module=" << frame.moduleName; } @@ -298,13 +150,13 @@ void AppendUIEditorRuntimeTrace( void AppendUIEditorCrashTrace( std::uint32_t exceptionCode, const void* exceptionAddress) { - AppendUIEditorCrashTrace(exceptionCode, exceptionAddress, nullptr); + AppendUIEditorCrashTrace(UIEditorCrashTrace{ + .exceptionCode = exceptionCode, + .exceptionAddress = exceptionAddress, + }); } -void AppendUIEditorCrashTrace( - std::uint32_t exceptionCode, - const void* exceptionAddress, - const _EXCEPTION_POINTERS* exceptionPointers) { +void AppendUIEditorCrashTrace(const UIEditorCrashTrace& trace) { std::lock_guard lock(g_traceMutex); if (!g_traceInitialized) { return; @@ -313,23 +165,22 @@ void AppendUIEditorCrashTrace( std::ostringstream header = {}; header << "Unhandled exception code=0x" << std::hex << std::uppercase << std::setw(8) << std::setfill('0') - << exceptionCode + << trace.exceptionCode << std::dec << std::setfill(' ') - << " address=" << FormatPointer(exceptionAddress); - const std::string exceptionModule = - ResolveModulePathFromAddress(reinterpret_cast(exceptionAddress)); - if (!exceptionModule.empty()) { - header << " module=" << exceptionModule; + << " address=" << FormatPointer(trace.exceptionAddress); + if (!trace.exceptionModule.empty()) { + header << " module=" << trace.exceptionModule; + } + if (trace.threadId != 0u) { + header << " threadId=" << trace.threadId; } - header << " threadId=" << GetCurrentThreadId(); const std::string headerText = header.str(); AppendTraceLine(g_crashTraceStream, "crash", headerText); AppendTraceLine(g_runtimeTraceStream, "crash", headerText); - const std::vector frames = CaptureExceptionStack(exceptionPointers); - for (std::size_t index = 0u; index < frames.size(); ++index) { - const std::string line = FormatCrashStackFrame(frames[index], index); + for (std::size_t index = 0u; index < trace.stackFrames.size(); ++index) { + const std::string line = FormatCrashStackFrame(trace.stackFrames[index], index); AppendTraceLine(g_crashTraceStream, "crash", line); AppendTraceLine(g_runtimeTraceStream, "crash", line); }