Skip to content

fix(v3/windows): revalidate WebView2 surface on cross-GPU monitor change (#5705)#5710

Draft
taliesin-ai wants to merge 1 commit into
masterfrom
worktree-fix-5705-cross-gpu-webview
Draft

fix(v3/windows): revalidate WebView2 surface on cross-GPU monitor change (#5705)#5710
taliesin-ai wants to merge 1 commit into
masterfrom
worktree-fix-5705-cross-gpu-webview

Conversation

@taliesin-ai

Copy link
Copy Markdown
Collaborator

Draft — addresses #5705. I cannot test this on real dual-GPU hardware, so the behavioural fix is unverified. Details and an explicit ask for verification are below.

What the issue reports

On a dual-GPU laptop (Intel iGPU internal panel + NVIDIA dGPU external panel), dragging a Wails v3 window from one monitor to the other turns the WebView2 content permanently black. The Go process survives, but every subsequent ExecuteScript returns RESOURCE_NOT_IN_CORRECT_STATE ("The group or resource is not in the correct state…"), so the frontend is dead until restart. Dragging back does not recover it.

Diagnosis

I traced this against the existing DPI-transition handling and I think the issue is a genuine gap, not a regression of the recent fixes:

Two honest caveats on the diagnosis:

  1. A commenter reports the opposite (crash only with a DPI mismatch, fine at equal DPI). I read that as evidence the underlying trigger is GPU-adapter surface loss, which is largely orthogonal to DPI — so I've deliberately not framed this as "mixed-DPI is already solved". Both triggers plausibly share the same adapter-change root cause.
  2. This may ultimately be a WebView2 runtime device-loss bug (dual-GPU black screens are a known class in the WebView2 backlog). A Wails-side workaround may only partially recover it.

On the issue's suggested fix

The issue proposes a WM_DISPLAYCHANGE handler. WM_DISPLAYCHANGE is only broadcast on a resolution/topology change — it does not reliably fire when a window is dragged between monitors. So the robust detector for the drag case is tracking the window's HMONITOR across WM_WINDOWPOSCHANGED (which does fire during the drag). I've implemented that as the primary path, and also kept a WM_DISPLAYCHANGE handler because on some hybrid-GPU laptops activating the dGPU for the external panel does cause a topology change.

What this PR does

  1. Track the window's current monitor handle; seed it at creation so only genuine crossings trigger work.
  2. On WM_WINDOWPOSCHANGED, when the handle changes (skipped while minimised, matching the [v3] App crashes when reopened after being minimized for 10~15 seconds on Windows #5605 reasoning), run a revalidation. Also run it on WM_DISPLAYCHANGE.
  3. The revalidation: NotifyParentWindowPositionChanged + rasterization-scale resync (no-op at equal DPI) + a bounds re-assert that actually produces a delta.

The part I most want review on

The revalidation action is the discriminating variable I can't test. Re-asserting the same bounds is a no-op (WebView2 early-outs on an unchanged rect — this is also why #5677 only re-asserts bounds after a scale change made layout stale). So I nudge the bounds by 1px and restore them to force a real re-layout / surface revalidation. That's the least-risky action that might actually do something, but I genuinely don't know whether it's enough to rebuild a composition surface stranded on a dead adapter.

Escalations, in increasing order of effectiveness and regression risk, that a hardware owner could try by editing revalidateWebviewForNewMonitor:

  • (this PR) 1px bounds nudge + restore — invisible, may not force a device rebuild.
  • Visibility togglecontroller.PutIsVisible(false) then true. More likely to force the compositor to re-attach, but risks a visible flash on every monitor crossing for all users.
  • Controller recreation — near-certain to recover, but heavy and loses page state.

Testing

  • Cross-compiles clean for windows/amd64 and windows/arm64; go vet shows only pre-existing unsafe.Pointer notes. Behaviour is unverified — I have no dual-GPU Windows machine.

Ask

@Lirt (#5705 reporter) and @qq540491950 — could you build this branch and confirm whether the 1px-nudge recovery clears the black screen on your hardware? If it doesn't, trying the visibility toggle (one-line change, noted in the code comment) would tell us whether a stronger surface reset is required. Maintainers: happy to reshape the recovery action or gate the visibility toggle behind an opt-in if that's the direction preferred.

@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a777bfe6-3b5c-4892-8625-61846f86997d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch worktree-fix-5705-cross-gpu-webview

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants