Skip to content

[Windows] WebView2 content disappears on mixed-DPI drag persists after #5689 — render process terminates on scale-up #5701

Description

@eleclin

First, thank you for #5689 — it successfully resolved the os.Exit(1) crash from #5650, and we appreciate the quick fix.

We'd like to share follow-up findings from testing #5689 on real mixed-DPI hardware. The crash is gone, but a visual artifact remains: after dragging a window across a mixed-DPI monitor boundary (scale-up direction, e.g. 100% → 150%), WebView2 content shrinks then disappears permanently until app restart.

We've done extensive diagnosis and believe the root cause is deeper than the missing Resize() that #5689 addressed. We'd like to share what we found and ask whether exposing ShouldDetectMonitorScaleChanges as a per-window option might be a feasible direction.

To Reproduce

  1. Wails v3 app on Windows, two monitors with different DPI (primary 1920×1080 @ 100%, secondary 1920×1080 @ 150%)
  2. Drag window from 100% monitor to 150% monitor
  3. Window freezes ~3 seconds, content briefly appears shrunk, then disappears entirely
  4. Only the window background colour remains; app must be restarted
  5. Reverse direction (150% → 100%) works fine

What we observed

We added debug logging to resyncWebviewRasterizationScale and the WM_DPICHANGED handler. On scale-up (100% → 150%):

WM_DPICHANGED enter dpi=144
  resync: dpiX=144 computedScale=1.5000 currentScale=1.0000   ← controller alive
  resync: PutRasterizationScale(1.5000) succeeded               ← COM returns S_OK
  resync returned true
  chromium.Resize() called                                      ← #5689 fix executes
WM_DPICHANGED exit

# 3 seconds later:
  resync: dpiX=144 computedScale=1.5000 currentScale=0.0000    ← unexpected: scale is 0
  resync: PutRasterizationScale(1.5000) succeeded               ← COM S_OK but no effect
  [WebView2] Eval failed: ERROR_INVALID_STATE  (continuous 10+ seconds)
  [WebView2] SetSize failed: ERROR_INVALID_STATE

The currentScale=0.0000 value appears to indicate the render process has terminated and failed to recreate. After that point, all COM calls (PutRasterizationScale, PutBounds, Resize, even Navigate) return S_OK but have no effect — the COM proxy seems to be talking to a dead render process.

Scale-down (150% → 100%) does not exhibit this: currentScale stays at valid values throughout.

What we tried

We explored several recovery approaches in the WM_DPICHANGED handler, but none could revive the render process once currentScale reached 0:

Approach Outcome
chromium.Resize() after resync (#5689) PutBounds also fails with ERROR_INVALID_STATE
Unconditional Resize() (ignoring resync gate) Same — PutBounds fails
Deferred retry with backoff (7 attempts, 500ms–15s) currentScale stays 0 across all retries
NotifyParentWindowPositionChanged() No effect
Navigate() to force render process recreation No effect — COM proxy appears non-functional

Our conclusion: once the render process terminates on scale-up, application-level COM calls cannot recover it. The issue needs to be prevented rather than recovered from.

What resolved it for us

We noticed PutShouldDetectMonitorScaleChanges(false) is set in chromium.go (line ~359), which we understand is intentional for the raw-pixels bounds mode. Out of curiosity, we tried enabling it (true) to let WebView2 handle DPI transitions natively, and it resolved the issue completely:

controller3.PutShouldDetectMonitorScaleChanges(true)

With native DPI detection enabled, the scale-up log looks like this — no currentScale=0, no errors:

WM_DPICHANGED enter dpi=144
  resync: dpiX=144 computedScale=1.5000 currentScale=1.0000   ← alive
  resync: PutRasterizationScale(1.5000) succeeded
WM_DPICHANGED exit
# No errors, content flashes once and recovers

Both drag directions now work correctly on our hardware.

To be transparent: we're currently running with a local patch
(go.mod replace directive) that enables this flag, which fully
resolves the issue for our use case. We're sharing these findings
in case they're useful — there's no urgency on our end, and we
respect that the maintainers may have considerations we're not
aware of.

A question about direction

We understand the raw-pixels-bounds-mode rationale for disabling native DPI detection by default, and we're not suggesting that default be changed. However, for developers on mixed-DPI multi-monitor setups, this behavior makes the app unusable without a workaround.

Would exposing ShouldDetectMonitorScaleChanges as a WebviewWindowOptions field (e.g. EnableNativeMonitorScaleDetection bool // default false) be a feasible approach? This would:

  • Preserve the existing default behavior and performance characteristics
  • Add zero regression risk (opt-in only)
  • Let affected developers self-resolve without maintaining a fork

We'd be happy to open a PR for this if the direction seems reasonable.

Environment

  • Wails: v3.0.0-alpha2.109
  • webview2 wrapper: github.com/wailsapp/wails/webview2@v1.0.27
  • OS: Windows 11 (Build 26200)
  • Monitors: 27" 1920×1080 @ 100% + 15" 1920×1080 @ 150%, extended display
  • Go: 1.26.2

Related issues

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions