Skip to content

fix(render): recover lock surface after resume - #3941

Open
meleeislandbot wants to merge 1 commit into
noctalia-dev:mainfrom
meleeislandbot:fix/3916-lock-surface-frame-callback
Open

fix(render): recover lock surface after resume#3941
meleeislandbot wants to merge 1 commit into
noctalia-dev:mainfrom
meleeislandbot:fix/3916-lock-surface-frame-callback

Conversation

@meleeislandbot

@meleeislandbot meleeislandbot commented Aug 14, 2026

Copy link
Copy Markdown

Summary

After suspend/resume, the compositor can stop presenting the lock surface: the wl_surface.frame callback registered before the stall never fires, and Surface::kickFrameLoop() keeps waiting for it. The lock screen then stops repainting, so password mask glyphs remain invisible even though input and authentication continue to work.

This PR keeps recovery scoped to the reproduced path:

  • Surface::discardPendingFrameCallback() destroys only the in-flight callback and transfers its tick intent to the replacement callback, without clearing queued frame work or update/layout/redraw state.
  • After resume, active lock surfaces discard their pending callback and request an immediate redraw.
  • The normal frame loop is unchanged: there is no timestamp or generic timeout policy.

Motivation

Closes #3916. On the affected host (RTX 5070 Ti / CachyOS / Hyprland), the previous fix 4dd6f29d re-rasterized glyph textures but remained blocked because no frame was rendered after resume.

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Refactoring
  • Build / packaging

Related Issue

Closes #3916

Testing

Targeted instrumented build on the affected host, with the generic timeout removed:

cycle replacement callback completed prompt updates after resume result
1 582 ms 16 mask visible, unlock OK
2 616 ms 16 mask visible, unlock OK
3 574 ms 17 mask visible, unlock OK

Each cycle recorded exactly one callback discard, followed by a replacement callback and continued prompt updates. There were zero errors and zero global stale-callback resets.

Clean candidate rebased onto d911d37e (main at test time):

  • structural recovery check: PASS;
  • release build: PASS;
  • Meson suite with -Dtests=enabled: 85/85 PASS;
  • just format with clang-format 22.1.8: no changes;
  • clean binary contains neither diagnostic logging nor the removed timeout policy;
  • clean build physical verification: 1/1 suspend/resume cycle passed (mask visible and updating, unlock normal, zero errors).

Manual Coverage

  • Tested on Niri
  • Tested on Hyprland
  • Tested on Sway
  • Tested on another compositor:
  • Tested with different bar positions and density settings
  • Tested at different interface scaling values
  • Tested with multiple monitors

Screenshots / Videos

N/A (fix restores expected rendering; no UI change).

Checklist

  • This PR is ready for review, or it is marked as Draft.
  • I read and followed the relevant guidance in CONTRIBUTING.md.
  • I ran just format with clang-format v22+ installed, or this PR has no code changes.
  • I ran the relevant build or test commands, or explained why they were not run.
  • I self-reviewed the changes.
  • I checked for new warnings or errors.
  • I will update end-user documentation after merge, or this PR does not change user-facing configuration or behavior.
  • I added or updated assets/translations/en.json, or this PR adds no new user-facing strings.
  • I did not edit non-English translation files unless this PR is explicitly for translation tooling, an import/export sync, or a maintainer-requested locale change.
  • I used the existing canonical names for config keys, IPC names, paths, and identifiers.

Additional Notes

  • Updated following Ly-sec's review: recovery is limited to active lock surfaces after resume, preserves queued work and tick intent, and does not impose a global callback deadline.
  • No diagnostic instrumentation is included in the PR.
  • Final diff: 5 files, 36 insertions.

@Ly-sec

Ly-sec commented Aug 15, 2026

Copy link
Copy Markdown
Member

Hey @meleeislandbot , thanks for the detailed investigation and testing. The targeted resume recovery looks useful, but I do not think the generic one-second timeout should be merged as written.

Wayland does not guarantee that a frame callback will arrive within one second. Hidden, occluded, or throttled surfaces may legitimately wait longer. The timestamp also starts when wl_surface_frame() is called, before rendering and commit, so the reported eglMakeCurrent stall can make a newly committed callback appear stale immediately.

resetStaleFrameCallback() also clears pending frame tick state, which could discard animation or data update intent. The timeout is not fully self-healing either, since it is only checked when another invalidation reaches kickFrameLoop().

I would keep this scoped to the reproduced resume path:

  1. Add a Surface method that discards the pending callback while preserving queued work and tick intent.
  2. Call it for active lock surfaces after resume.
  3. Request a redraw so the glyph invalidation is applied immediately.
  4. Remove the timestamp and generic timeout from kickFrameLoop().

The diagnosis and hardware verification are excellent. I think the targeted portion of the fix is the right direction, but the global stale-callback policy is broader than this bug requires.

@meleeislandbot
meleeislandbot force-pushed the fix/3916-lock-surface-frame-callback branch from 1002b2e to 7cf1f8d Compare August 15, 2026 20:20
@meleeislandbot meleeislandbot changed the title fix(render): restart lock surface frame loop after resume fix(render): recover lock surface after resume Aug 15, 2026
@meleeislandbot

Copy link
Copy Markdown
Author

Thanks — I reworked the PR to follow that exact scope.

  • Removed the callback timestamp and generic one-second policy from kickFrameLoop().
  • Added Surface::discardPendingFrameCallback(), which transfers m_frameCallbackShouldTick to m_nextFrameCallbackShouldTick and leaves queued frame work, pending ticks, and update/layout/redraw state intact.
  • Recovery now runs only for active lock surfaces after resume, followed by requestRedraw().

I tested this targeted variant on the affected RTX 5070 Ti / Hyprland host. It passed 3/3 suspend/resume cycles: one targeted discard per cycle, replacement callbacks completed in 582/616/574 ms, prompt updates continued (16/16/17), mask glyphs stayed visible, and unlock succeeded. There were no errors and no global stale-callback resets.

The clean candidate was rebased onto current main (d911d37e), passed the structural check, release build, 85/85 Meson tests, and just format with clang-format 22.1.8. Clean-build physical verification also passed 1/1 suspend/resume cycle (mask visible and updating, unlock normal, zero errors).

Could you take another look when convenient?

After suspend/resume, a lock surface can keep waiting for a Wayland frame callback registered before the stall. The password field then stops repainting even though input and authentication still work.

Add an explicit callback discard that transfers the in-flight tick intent and leaves queued work and dirty state intact. Invoke it only for active lock surfaces after resume, then request a redraw. The normal frame loop remains unchanged; there is no generic timeout.

Verified on the affected RTX 5070 Ti / Hyprland host: the targeted instrumented variant passed 3/3 suspend/resume cycles, and the clean current-main build passed 85/85 tests plus 1/1 physical cycle.
@meleeislandbot
meleeislandbot force-pushed the fix/3916-lock-surface-frame-callback branch from 7cf1f8d to e2c659e Compare August 15, 2026 20:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Password mask glyphs disappear after suspend/resume but authentication succeeds

2 participants