Closed Bug 2018866 Opened 7 months ago Closed 7 months ago

[Wayland] After wl_output destroy/recreate (monitor power-cycle), HW compositing is permanently disabled and Display0 reports 0x0 @ 0.000 scale — new tabs render blank until restart

Categories

(Core :: Widget: Gtk, defect, P3)

Firefox 147
x86_64
Linux
defect

Tracking

()

RESOLVED FIXED
150 Branch
Tracking Status
firefox150 --- fixed

People

(Reporter: hello, Assigned: stransky)

References

(Blocks 1 open bug)

Details

Attachments

(15 files)

1.76 MB, image/png
Details
2.06 MB, image/png
Details
1.72 MB, image/png
Details
957.82 KB, image/png
Details
991.91 KB, image/png
Details
1009.39 KB, image/png
Details
552.19 KB, text/plain
Details
36.87 KB, text/plain
Details
2.03 MB, text/plain
Details
40.83 KB, text/plain
Details
155.83 KB, application/gzip
Details
221.51 KB, application/gzip
Details
48 bytes, text/x-phabricator-request
Details | Review
48 bytes, text/x-phabricator-request
Details | Review
48 bytes, text/x-phabricator-request
Details | Review

Background

On COSMIC desktop, monitor power-cycle events (DPMS off/on or physical power button) cause the compositor to destroy the wl_output global and create a new one. This is valid Wayland protocol behavior — clients are expected to handle output removal and re-addition. See https://github.com/pop-os/cosmic-comp/pull/2116 for compositor-side context.

When this sequence occurs, Firefox permanently disables hardware compositing for the session. New tabs opened after reconnect render blank (white/empty). Existing tabs continue to function. Only a full Firefox restart recovers rendering.

Environment

  • Machine: Minisforum MS-01 mini workstation
  • CPU: Intel Core i9-13900H (Raptor Lake-P, Iris Xe Graphics)
  • GPU driving display: Intel i915 / Iris Xe — sole display GPU
  • Display: Single HDMI-A-1 monitor connected to i915 (NVIDIA RTX 4000 SFF Ada also present but compute-only, no displays)
  • Compositor: COSMIC (cosmic-comp 0.1177134409824.04~ef14b7e)
  • Mesa: 25.1.5 (mesa/iris driver)
  • Kernel: 6.18.7-76061807-generic
  • Firefox: 147.0.4 (native, /usr/bin/firefox)
  • OS: Pop!_OS 24.04

Reproduction Steps

Trigger A — COSMIC power saving (most reliable):

  1. Open Firefox, load at least one tab
  2. Go to COSMIC Settings → Power & Battery → Power Saving Options
  3. Set "Turn off the screen after" to 1 minute
  4. Wait for screen to power off
  5. Move mouse or press key to wake display
  6. Open a new tab and navigate to any URL

Trigger B — Physical monitor power button:

  1. Open Firefox, load at least one tab
  2. Press the physical power button on the monitor (off)
  3. Wait ~30 seconds
  4. Power monitor back on
  5. Open a new tab and navigate to any URL

Observed result: New tab content area remains blank/white. The tab title, URL bar, and browser chrome render correctly. No crash occurs. Existing tabs continue to render correctly.

Expected result: New tabs render normally after monitor reconnect.

Diagnostic Evidence

about:support → Graphics section after trigger (captured live with blank tab open):

Compositing: WebRender (Software) ← was hardware before trigger
Display0: 0x0@60Hz scales:0.000000|0.000000 ← ZERO dimensions/scale

Decision Log after trigger:

HW_COMPOSITING default=available
user=disabled "Disabled by layers.acceleration.disabled=true"
failure: FEATURE_FAILURE_COMP_PREF

WEBRENDER default=available
runtime=unavailable-no-hw-compositing
"Hardware compositing is disabled"
failure: FEATURE_FAILURE_WEBRENDER_NEED_HWCOMP

WEBRENDER_COMPOSITOR env=blocklisted "Blocklisted by gfxInfo"
failure: FEATURE_FAILURE_WEBRENDER_COMPOSITOR_DISABLED

Before the trigger, hardware compositing is active and Display0 shows correct dimensions.

Analysis

The failure has two components:

  1. Display dimensions not re-read on wl_output recreate: After the new wl_output global is announced by the compositor, Firefox records Display0 as 0x0@60Hz scales:0.000000|0.000000. It appears the output geometry/scale events from the new wl_output object are not being applied, or the stale zero-values from the destroyed output are persisting.

  2. Hardware compositing permanently disabled for session: After the wl_output removal, layers.acceleration.disabled=true is set (possibly as a consequence of a GPU process crash during the removal sequence). This is not reset when the output reappears. With HW compositing disabled, WebRender falls back to software mode, and new surfaces cannot be properly composited.

MOZ_LOG Evidence

Captured with MOZ_LOG="Widget:5,GPUProcessManager:5,GPUProcessHost:5,CanvasTranslator:5,GLContext:3".

Supplementary observation from LibreWolf (different product, same Gecko engine — additional signal only): In LibreWolf 146.0.1-1, CanvasTranslator failed creating WebGL shared context appears in the parent process crash annotation at the moment of monitor reconnect, followed by libva re-initialization — suggesting the GPU process crashes and restarts. The Firefox-native MOZ_LOG capture is attached, but the GPU process sandbox prevents capturing GPU-process stderr via tee; this specific annotation was only visible in LibreWolf's output.

Additional Notes

  • The bug does NOT cause a visible Firefox crash
  • about:crashes shows no relevant entries
  • No crash minidumps generated
  • GPU #2 (NVIDIA 0x10de:0x27b0) present but inactive/compute-only; DMABUF_SURFACE_EXPORT blocklisted (FEATURE_FAILURE_BROKEN_DRIVER) — both pre-existing, unrelated to this bug
  • Reproduced identically across Firefox 147.0.4 (native) and LibreWolf 146.0.1-1 (Flatpak, based on Firefox 146)
  • Workaround: restart Firefox after monitor reconnect

Related

Can you run on terminal with MOZ_LOG="WidgetScreen:5" log, reproduce the issue and attach the log here?
Thanks.

Flags: needinfo?(hello)
Here's the MOZ_LOG="WidgetScreen:5" capture (attached). The key sequence: 1. **Before trigger** (lines 1-245): Healthy — Monitor 0 is 3840x2160, scale 1.750228. 2. **Line 246**: `Received monitors-changed event` — the wl_output destroy fires. 3. **Line 248**: `ScreenGetterGtk() ... monitor num 0` — sees **zero monitors** (output gone). 4. **Lines 255-261**: Second `monitors-changed` event (wl_output recreate). Now sees 1 monitor, but geometry/scale events haven't been applied yet: ``` Monitor 0 uses fractional scale 0.000000 New monitor 0 size [0,0 -> 0 x 0] depth 24 scale 0.000000 CssScale 0.000000 DPI 0.000000 refresh 60 HDR 0] ``` 5. **Line 272 onward**: All `GetScreenForWindow()` calls return `w=0, h=0` — stuck at zero permanently. 6. **Lines 280-288**: A third `monitors-changed` event fires, still reads 0x0 and scale 0. Never recovers. It looks like `ScreenGetterGtk` reads the monitor properties from the new `wl_output` before the compositor has sent the geometry/mode/scale/done events, so everything comes back as zero.

Can you run on terminal with WAYLAND_DEBUG=1 MOZ_LOG="WidgetScreen:5" log, reproduce the issue and attach the log here? It adds Wayland protocol logging there.
Thanks.

Priority: -- → P3
Attached: wayland_debug_log.txt (WAYLAND_DEBUG=1 MOZ_LOG="WidgetScreen:5", 31637 lines) Reproduced by pressing the monitor's power button (standby → wake). Here's the timeline from the log around the failure: Healthy state (line 297): Monitor 0 uses fractional scale 1.750228 New monitor 0 size [0,0 -> 3840 x 2160] scale 1.750228 Monitor power-cycle — destroy (line 18911–18924): wl_surface#61.leave(wl_output#16) wl_surface#52.leave(wl_output#16) wl_registry#2.global_remove(59) → Received monitors-changed event → ScreenGetterGtk() monitor num 0 ← zero monitors, expected New output arrives ~739ms later (line 18929–19015): wl_registry#2.global(61, "wl_output", 4) → bind as wl_output#78 wl_output#78.geometry(0, 0, 600, 340, "Dell Inc.", "DELL U2723QE", 0) wl_output#78.mode(3, 3840, 2160, 60000) ← correct wl_output#78.scale(2) ← correct wl_output#78.done() monitors-changed fires — but all zeros (line 19016–19022): Received monitors-changed event ScreenGetterGtk() monitor num 1 ← sees 1 monitor Monitor 0 uses fractional scale 0.000000 ← but scale is zero New monitor 0 size [0,0 -> 0 x 0] depth 24 scale 0.000000 The wl_output protocol delivered correct geometry, mode, and scale before the done() — but ScreenGetterGtk reads all zeros from the GdkMonitor. xdg_output arrives, second done() — still zeros (line 19048–19059): zxdg_output_v1#79.logical_size(2194, 1234) ← correct wl_output#78.done() ← second done → Received monitors-changed event → Monitor 0 uses fractional scale 0.000000 ← still zero → New monitor 0 size [0,0 -> 0 x 0] ← still zero Never recovers. GetGTKMonitorFractionalScaleFactor(0) returns 0.000000 for the rest of the session (e.g., lines 21314, 30405, 31034). All GetScreenForWindow() calls return w=0, h=0. One observation that might be relevant: the healthy fractional scale is 1.750228, not 2.0 — so it comes from a different source than wl_output.scale(2). Whatever that source is, it doesn't recover after the output recreation.
Flags: needinfo?(stransky)
Flags: needinfo?(hello)

We'd need more logs here how the monitor area is obtained.

Depends on: 2019552
I see you've landed additional MakeScreenGtk() logging in bug 2019552 (commit c95a362bba60). That looks like exactly the instrumentation needed to trace the monitor area read path. I'll reproduce with a Nightly build that includes that patch and attach the new log. Is there anything else you'd like me to capture at the same time?

Yes, please do the WAYLAND_DEBUG=1 MOZ_LOG="WidgetScreen:5" log again with latest nightly.
Thanks.

Flags: needinfo?(stransky) → needinfo?(hello)
Blocks: wayland
Reproduced on Nightly build 20260227090653 with the new MakeScreenGtk() logging from bug 2019552. Log attached. Healthy startup (line 2-4): MakeScreenGtk() Monitor [0] scale 2 aIsHDR 0 workarea [0, 0] -> [4388 x 2468] MonitorConfig pixel size [0, 0] -> [3840 x 2160] After hotplug — second monitors-changed (line 221-224): MakeScreenGtk() Monitor [0] scale 2 aIsHDR 0 workarea [0, 0] -> [7680 x 4320] MonitorConfig pixel size [0, 0] -> [0 x 0] After hotplug — third monitors-changed (line 250-253): MakeScreenGtk() Monitor [0] scale 2 aIsHDR 0 workarea [0, 0] -> [4388 x 2468] MonitorConfig pixel size [0, 0] -> [0 x 0] MonitorConfig pixel size goes to 0x0 after the hotplug event and never recovers. The workarea also briefly doubles to 7680x4320 (exactly 2x native) on the first read before correcting.
Reattaching with WAYLAND_DEBUG=1 as originally requested (log_04.txt was missing it). This log (log_05.txt) supersedes log_04.txt — same reproduction, same results, with Wayland protocol trace interleaved.

Cool. Looks like we have an issue with Wayland display monitor setup. Please run with:

WAYLAND_DEBUG=1 MOZ_LOG="WidgetScreen:5,WidgetWayland:5" env variables

to get info about monitor changes (there will be 'nsWaylandDisplay ID ...' lines related to monitor changes there).
Thanks.

It should be something like:

[(null) 709496: Main Thread]: D/WidgetWayland nsWaylandDisplay ID 3 geometry position 0 0 physical size 640 360 subpixel 0 transform 0
[(null) 709496: Main Thread]: D/WidgetWayland nsWaylandDisplay ID 3 mode output size 3840 x 2160
[Parent 709496: Main Thread]: D/WidgetWayland nsWaylandDisplay::GetMonitorConfig() 0, 0 matches

in the widget wayland log.

Flags: needinfo?(stransky)
Here's the WAYLAND_DEBUG=1 MOZ_LOG="WidgetScreen:5,WidgetWayland:5" capture from Nightly 20260227. Log attached.

Yes, so the bug is caused by unfortunate event order as the wl_output owned by Firefox is filed after gtk3 ones. So we get monitor-changed event but we don't have monitor info available yet.

Flags: needinfo?(hello)

Looking at the difference between cosmic and mutter. Mutter doesn't remove/add wl_output when resolution/monitor changes. Only issues new wl_output.mode() / zxdg_output_v1.logical_size events.

A solution may be to listen on wl_output changes only and use Gtk3 signals for X11.

Flags: needinfo?(stransky)
Assignee: nobody → stransky
Status: UNCONFIRMED → ASSIGNED
Ever confirmed: true
**Additional symptom: GTK menu popups don't render after 0x0 latch** After the monitor wake triggers the 0x0 state, Firefox's menu bar appears on Alt, but clicking any menu item produces no dropdown. The popup surfaces appear to be sized from the latched 0x0 display dimensions. Same setup as the original report — COSMIC compositor, Firefox Nightly.
Status: ASSIGNED → RESOLVED
Closed: 7 months ago
Resolution: --- → FIXED
Target Milestone: --- → 150 Branch
Regressions: 2026258
Regressions: 2042308

A patch has been attached on this bug, which was already closed. Filing a separate bug will ensure better tracking. If this was not by mistake and further action is needed, please alert the appropriate party. (Or: if the patch doesn't change behavior -- e.g. landing a test case, or fixing a typo -- then feel free to disregard this message)

You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: