From 42cff6de4a9f02ae11ff3144536738297791db42 Mon Sep 17 00:00:00 2001 From: fanlusky <1730976608@qq.com> Date: Sun, 27 Sep 2026 10:24:50 +0800 Subject: [PATCH] =?UTF-8?q?fix(server):=20WebView2=20=E9=80=89=E8=AF=8D?= =?UTF-8?q?=E4=B8=8D=E5=86=8D=E7=99=BD=E7=AD=89=E6=B8=B2=E6=9F=93=E5=90=8C?= =?UTF-8?q?=E6=AD=A5=E8=B6=85=E6=97=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WebView2 下几乎每次空格/数字选词都等满渲染同步上限(日志 waited_ms 82-94, rendered 恒比 current 小 1),体感为上屏顿一下。 每个按键 worker 会以相同内容发布两次页面:第一次走 content-only,回显在 异步 DOM 回调里;第二次命中 dedup,立即回显新代数。随后迟到的 DOM 回调把 回显改回旧代数,选词就一直等到超时。D2D 同步回显,不受影响。 - 渲染回显代数只进不退(AdvanceRenderedCandidateGeneration),迟到的旧回显 不再覆盖新回显。 - folded-inflight 分支补上回显:进行中的 FineTune 只回显它捕获的那一页, 之后折叠进来的页面此前永远没有回显。 --- server/src/global/globals.h | 6 +++++- server/src/ipc/candidate_render_sync.h | 9 +++++++++ server/src/window/ime_windows.cpp | 11 ++++++++++- server/tests/src/test_candidate_render_sync.cpp | 17 +++++++++++++++++ 4 files changed, 41 insertions(+), 2 deletions(-) diff --git a/server/src/global/globals.h b/server/src/global/globals.h index 31823a44..ab228475 100644 --- a/server/src/global/globals.h +++ b/server/src/global/globals.h @@ -12,6 +12,7 @@ #include #include #include "engine/core/word_item.h" +#include "ipc/candidate_render_sync.h" #include "window/candidate_view_model.h" namespace GlobalIme @@ -256,7 +257,10 @@ inline void PublishRenderedCandidatePageGeneration(std::uint64_t generation) { { std::lock_guard lock(candidate_render_mutex); - rendered_candidate_page_generation.store(generation, std::memory_order_release); + rendered_candidate_page_generation.store( + FanyImeIpc::AdvanceRenderedCandidateGeneration( + rendered_candidate_page_generation.load(std::memory_order_relaxed), generation), + std::memory_order_release); } candidate_render_cv.notify_all(); } diff --git a/server/src/ipc/candidate_render_sync.h b/server/src/ipc/candidate_render_sync.h index 9f4bac36..ffac3d35 100644 --- a/server/src/ipc/candidate_render_sync.h +++ b/server/src/ipc/candidate_render_sync.h @@ -36,4 +36,13 @@ constexpr bool ShouldWaitForCandidateRender(std::uint64_t rendered, std::uint64_ } return rendered < current; } + +// The render echo only moves forward. WebView2 echoes from async DOM callbacks, so a content-only +// update's callback can land after a dedup of the same content already echoed the newer generation; +// storing the older one left rendered one behind published and every selection waited out the full +// bound. +constexpr std::uint64_t AdvanceRenderedCandidateGeneration(std::uint64_t rendered, std::uint64_t echoed) noexcept +{ + return echoed > rendered ? echoed : rendered; +} } // namespace FanyImeIpc diff --git a/server/src/window/ime_windows.cpp b/server/src/window/ime_windows.cpp index 13fead4d..e6c53990 100644 --- a/server/src/window/ime_windows.cpp +++ b/server/src/window/ime_windows.cpp @@ -2492,7 +2492,16 @@ LRESULT CALLBACK WndProcCandWindow(HWND hwnd, UINT message, WPARAM wParam, LPARA { CAND_DIAG_LOGF(L"show folded into in-flight fine-tune caret=({},{})", Global::Point[0], Global::Point[1]); CAND_DIAG_LOGF(L"candidate-frame path=folded-inflight content_gen={}", contentGeneration); - InflateCandWnd(str); + // The in-flight FineTune echoes only the page it captured, which may predate this one. Without + // an echo here a digit/space selection right after a fast burst waits out the full render-sync + // timeout, felt as a stall on commit. + InflateCandWnd(str, [contentGeneration, pageGeneration = candidatePage->generation]() { + if (!::is_global_wnd_cand_shown || contentGeneration != g_candidate_content_generation.load()) + { + return; + } + Global::PublishRenderedCandidatePageGeneration(pageGeneration); + }); KillTimer(hwnd, TIMER_ID_CANDIDATE_MOVE_SETTLE); SetTimer(hwnd, TIMER_ID_CANDIDATE_MOVE_SETTLE, kCandidateMoveSettleMs, nullptr); return 0; diff --git a/server/tests/src/test_candidate_render_sync.cpp b/server/tests/src/test_candidate_render_sync.cpp index b2a9edc2..0e3fdbbc 100644 --- a/server/tests/src/test_candidate_render_sync.cpp +++ b/server/tests/src/test_candidate_render_sync.cpp @@ -22,6 +22,23 @@ TEST_CASE(candidate_render_sync_waits_only_when_a_painted_page_lags_the_publishe REQUIRE(!ShouldWaitForCandidateRender(8, 7, false, true)); } +TEST_CASE(candidate_render_echo_never_moves_backwards) +{ + using FanyImeIpc::AdvanceRenderedCandidateGeneration; + using FanyImeIpc::ShouldWaitForCandidateRender; + + // WebView2: a dedup echoes 332 synchronously, then the content-only DOM callback for the same + // content lands and echoes 331. The stale echo must not pull rendered back behind published. + std::uint64_t rendered = 330; + rendered = AdvanceRenderedCandidateGeneration(rendered, 332); + rendered = AdvanceRenderedCandidateGeneration(rendered, 331); + REQUIRE_EQ(rendered, 332u); + REQUIRE(!ShouldWaitForCandidateRender(rendered, 332, false, true)); + + // A newer echo still advances. + REQUIRE_EQ(AdvanceRenderedCandidateGeneration(332, 333), 333u); +} + TEST_CASE(candidate_render_sync_wait_is_bounded_to_a_visible_stall) { // The bound is a UX contract and a transport constraint: a wedged UI thread may delay a