From dcede7f9756901b5a0a5ef72ca47951ccfa328e3 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Wed, 22 Apr 2026 20:34:31 +0800 Subject: [PATCH] fix(new_editor): finalize synchronous resize presentation cleanup --- ...entationRootFixPlan_完成归档_2026-04-22.md | 118 ++++++++++++++++++ .../Win32/EditorWindowFrameDriver.cpp | 2 - .../Platform/Win32/EditorWindowFrameRequest.h | 22 ---- .../EditorWindowMessageDispatcher.h | 1 + 4 files changed, 119 insertions(+), 24 deletions(-) create mode 100644 docs/used/NewEditor_FrameResizeSynchronousPresentationRootFixPlan_完成归档_2026-04-22.md delete mode 100644 new_editor/app/Platform/Win32/EditorWindowFrameRequest.h diff --git a/docs/used/NewEditor_FrameResizeSynchronousPresentationRootFixPlan_完成归档_2026-04-22.md b/docs/used/NewEditor_FrameResizeSynchronousPresentationRootFixPlan_完成归档_2026-04-22.md new file mode 100644 index 00000000..7dd9f063 --- /dev/null +++ b/docs/used/NewEditor_FrameResizeSynchronousPresentationRootFixPlan_完成归档_2026-04-22.md @@ -0,0 +1,118 @@ +# NewEditor Frame Resize Synchronous Presentation Root Fix Plan + +Date: 2026-04-22 +Status: Completed Archive + +## 1. Objective + +Restore the original `new_editor` resize-time anti-deformation contract without adding fallback branches, duplicated resize paths, or new ownership confusion. + +The fix must re-establish one clean rule: + +1. when a host-side transition needs a new window size before the native window visibly adopts it, the window content must be rendered synchronously for that target size +2. when Win32 enters paint / size / DPI lifecycle messages, the host must be able to drive a synchronous frame before returning control +3. normal steady-state rendering must remain on the centralized frame driver path + +## 2. Confirmed Root Cause + +The regression was introduced by commit `b44f5ca9` (`refactor(new_editor): centralize win32 frame execution`). + +That refactor removed the synchronous render step from the critical resize path: + +1. `EditorWindowChromeController::HandleResizePointerMove(...)` +2. `EditorWindowChromeController::ApplyPredictedWindowRectTransition(...)` +3. `EditorWindowMessageDispatcher` handling of `WM_SIZE`, `WM_DPICHANGED`, `WM_EXITSIZEMOVE`, and `WM_PAINT` + +and replaced it with deferred `RequestFrame(...)` bookkeeping. + +That bookkeeping does not preserve the old contract because: + +1. `RequestFrame(...)` only records a bitmask +2. rendering now happens later from the app main loop +3. the window can already be resized before the new frame is presented +4. the old presented surface is therefore stretched by the platform / swap chain path + +This means the actual regression is not the custom title bar itself. The regression is the loss of synchronous presentation ownership in the Win32 host lifecycle. + +## 3. Red Lines + +The implementation must not: + +1. add a second resize algorithm beside the current borderless resize path +2. add ad hoc `if (interactiveResize)` branches throughout unrelated rendering code +3. keep a dead frame-request state machine that no longer owns rendering policy +4. duplicate `RenderFrame(...)` orchestration in multiple modules +5. touch workspace transfer / docking semantics unrelated to resize-time presentation + +## 4. Target End State + +After the fix: + +1. `EditorWindowFrameDriver` remains the single general-purpose frame execution entry point +2. synchronous Win32 lifecycle rendering uses that same driver wherever possible +3. `WM_PAINT` remains a special case only because it requires `BeginPaint` / `EndPaint` +4. borderless predicted transitions render the target-size frame synchronously before `SetWindowPos(...)` +5. dead `RequestFrame(...)` / `EditorWindowFrameRequestReason` plumbing is removed if it no longer owns any real policy + +## 5. Implementation Plan + +### Phase A. Remove the dead deferred frame-request layer + +Completed on 2026-04-22. + +1. removed `EditorWindowFrameRequestReason` +2. removed `EditorWindow::RequestFrame(...)` +3. removed `EditorWindow::ConsumePendingFrameRequestReasons()` +4. removed all call sites that only existed to defer rendering into the main loop + +### Phase B. Rebuild one synchronous frame-execution helper path + +Completed on 2026-04-22. + +1. kept `EditorWindowFrameDriver::DriveFrame(...)` as the shared frame executor +2. added a dispatcher-local helper that drives a frame immediately and forwards transfer requests to `EditorWindowWorkspaceCoordinator` +3. restored that helper to `WM_SIZE`, `WM_DPICHANGED`, and `WM_EXITSIZEMOVE` + +### Phase C. Restore paint-time synchronous rendering + +Completed on 2026-04-22. + +1. restored `WM_PAINT` to render synchronously inside the paint handling path +2. kept the paint special case local to `BeginPaint` / `EndPaint` + +### Phase D. Restore predicted-transition pre-presentation + +Completed on 2026-04-22. + +1. `HandleResizePointerMove(...)` once again renders the target-size frame synchronously before `SetWindowPos(...)` +2. `ApplyPredictedWindowRectTransition(...)` does the same for maximize / restore style transitions +3. this was restored on the original transition path without adding a second resize subsystem + +### Phase E. Verify no unrelated ownership was widened + +Completed on 2026-04-22. + +1. workspace transfer / docking semantics were left untouched +2. `EditorWindowHostRuntime::RenderAllWindows(...)` still owns steady-state frame pumping +3. only Win32 host lifecycle and predicted transition code were changed + +## 6. Validation Requirements + +Validation completed on 2026-04-22: + +1. `XCUIEditorApp` Debug build passed after the fix +2. user verified the resize deformation regression was gone in actual runtime + +Pending after this archive: + +1. follow-up investigation for remaining resize jitter +2. any later refinement should stay on the same host lifecycle boundary and must not reintroduce a deferred fake scheduling layer + +## 7. Completion Criteria + +This work was completed with the following outcomes: + +1. the regression source introduced by `b44f5ca9` was structurally removed +2. one clear synchronous presentation path was restored for resize-time lifecycle transitions +3. the dead deferred request layer was removed +4. the resulting code path remained a mainline host-lifecycle design instead of a post hoc fallback patch diff --git a/new_editor/app/Platform/Win32/EditorWindowFrameDriver.cpp b/new_editor/app/Platform/Win32/EditorWindowFrameDriver.cpp index eddca115..92d9ee33 100644 --- a/new_editor/app/Platform/Win32/EditorWindowFrameDriver.cpp +++ b/new_editor/app/Platform/Win32/EditorWindowFrameDriver.cpp @@ -14,8 +14,6 @@ EditorWindowFrameTransferRequests EditorWindowFrameDriver::DriveFrame( return {}; } - (void)window.ConsumePendingFrameRequestReasons(); - EditorWindowFrameTransferRequests transferRequests = window.RenderFrame(editorContext, globalTabDragActive); if (const HWND hwnd = window.GetHwnd(); diff --git a/new_editor/app/Platform/Win32/EditorWindowFrameRequest.h b/new_editor/app/Platform/Win32/EditorWindowFrameRequest.h deleted file mode 100644 index 5662675e..00000000 --- a/new_editor/app/Platform/Win32/EditorWindowFrameRequest.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include - -namespace XCEngine::UI::Editor::App { - -enum class EditorWindowFrameRequestReason : std::uint32_t { - None = 0u, - Initial = 1u << 0u, - PaintMessage = 1u << 1u, - Resize = 1u << 2u, - DpiChanged = 1u << 3u, - ExitSizeMove = 1u << 4u, - BorderlessTransition = 1u << 5u, - ManualScreenshot = 1u << 6u -}; - -constexpr std::uint32_t ToFrameRequestMask(EditorWindowFrameRequestReason reason) { - return static_cast(reason); -} - -} // namespace XCEngine::UI::Editor::App diff --git a/new_editor/app/Platform/Win32/WindowManager/EditorWindowMessageDispatcher.h b/new_editor/app/Platform/Win32/WindowManager/EditorWindowMessageDispatcher.h index 895115b4..1b5a943c 100644 --- a/new_editor/app/Platform/Win32/WindowManager/EditorWindowMessageDispatcher.h +++ b/new_editor/app/Platform/Win32/WindowManager/EditorWindowMessageDispatcher.h @@ -32,6 +32,7 @@ public: private: struct DispatchContext; + static void RenderAndHandleWindowFrame(const DispatchContext& context); static bool EnsureTrackingMouseLeave(const DispatchContext& context); static bool TryHandleChromeHoverConsumption( const DispatchContext& context,