WebView2 下几乎每次空格/数字选词都等满渲染同步上限(日志 waited_ms 82-94, rendered 恒比 current 小 1),体感为上屏顿一下。 每个按键 worker 会以相同内容发布两次页面:第一次走 content-only,回显在 异步 DOM 回调里;第二次命中 dedup,立即回显新代数。随后迟到的 DOM 回调把 回显改回旧代数,选词就一直等到超时。D2D 同步回显,不受影响。 - 渲染回显代数只进不退(AdvanceRenderedCandidateGeneration),迟到的旧回显 不再覆盖新回显。 - folded-inflight 分支补上回显:进行中的 FineTune 只回显它捕获的那一页, 之后折叠进来的页面此前永远没有回显。
50 lines
2.4 KiB
C++
50 lines
2.4 KiB
C++
#include "ipc/candidate_render_sync.h"
|
|
#include "tests/includes/test_framework.h"
|
|
|
|
TEST_CASE(candidate_render_sync_waits_only_when_a_painted_page_lags_the_published_page)
|
|
{
|
|
using FanyImeIpc::ShouldWaitForCandidateRender;
|
|
|
|
// Already painted: no wait, so normal typing pays nothing.
|
|
REQUIRE(!ShouldWaitForCandidateRender(7, 7, false, true));
|
|
// A newer page was published while the painted frame is older: wait for the paint.
|
|
REQUIRE(ShouldWaitForCandidateRender(6, 7, false, true));
|
|
REQUIRE(ShouldWaitForCandidateRender(1, 2, false, true));
|
|
// Generation 0 means nothing has been published or painted yet.
|
|
REQUIRE(!ShouldWaitForCandidateRender(0, 7, false, true));
|
|
REQUIRE(!ShouldWaitForCandidateRender(7, 0, false, true));
|
|
REQUIRE(!ShouldWaitForCandidateRender(0, 0, false, true));
|
|
// Host-drawn (UI-less) candidate windows have no on-screen list to match.
|
|
REQUIRE(!ShouldWaitForCandidateRender(6, 7, true, true));
|
|
// A hidden candidate window has no on-screen list to match either.
|
|
REQUIRE(!ShouldWaitForCandidateRender(6, 7, false, false));
|
|
// A render echoed ahead of the published page is not a lag (page cleared and republished).
|
|
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
|
|
// committed keystroke by at most this, and the wait must stay well inside the TSF commit reply
|
|
// timeout (300ms, windows/src/IPC/Ipc.h FANY_IME_COMMIT_REPLY_TIMEOUT_MS) or TSF tears the pipe
|
|
// down and the user has to choose again.
|
|
REQUIRE_EQ(FanyImeIpc::kCandidateSelectionRenderWaitMaxMs, 80);
|
|
}
|