Skip to content

fix(gpui-linux): demand-driven Wayland loop and font-match prewarm - #7

Merged
freefcw merged 4 commits into
develop/0.9from
cursor/wayland-loop-font-cache-a5ca
Aug 29, 2026
Merged

fix(gpui-linux): demand-driven Wayland loop and font-match prewarm#7
freefcw merged 4 commits into
develop/0.9from
cursor/wayland-loop-font-cache-a5ca

Conversation

@freefcw

@freefcw freefcw commented Aug 29, 2026

Copy link
Copy Markdown
Owner

Selective ports of two Zed Linux GPUI fixes onto develop/0.9. Not a wholesale Zed sync.

Rebased onto 97bc23905fb9c54ad4a766ad748fa3bd4e8c829e after #4 (Taffy), #5 (container_query + axis-locked scroll), and #6 (appearance override, deferred appearance callbacks, const HSLA, macOS NSAppearance unsafe) merged. One conflict in crates/gpui/src/app.rs test imports: kept this PR’s refresh-test types (Context, Empty, Render, Window) and #6’s WindowAppearance. platform.rs and window.rs auto-merged. #3’s quit_requested skip and #6’s set_window_appearance / deferred appearance observers are unchanged.

1. Wayland demand-driven render loop

Upstream: zed-industries/zed eb354c8d504071bdb79110a7a5c9d374c2864113 (#60690)

Wayland wl_surface frame callbacks only arrive after a commit the compositor actually paints. The old loop requested a callback on every tick and committed empty frames as a heartbeat, which froze idle fullscreen windows and burned CPU.

This port parks when idle and wakes via schedule_frame() / a calloop ping / a 60Hz retry timer. LayerShell, overlay, click-through, Application::new(), QuitMode, and GPU resource budgets are unchanged.

Local commit: 554eff6885aa29252a8c35dad3f0f6121b16665c
Author/committer: Jun He freefcw@gmail.com

2. Prewarm Linux font-match caches

Upstream: f1d27d545e79f98ffde7ebf3229f8eb8bad791aa (#63158)

Adds PlatformTextSystem::prewarm_fonts / TextSystem::prewarm_fonts and implements it on the Linux cosmic-text backend so get_font_matches is warmed off the shaping hot path. The text stack is not rewritten.

Local commit: ab2fb46c245f4552f2c5f7df70e70edcdc70801f
Author/committer: Jun He freefcw@gmail.com

Review fixes (ec811a9688a54b95b8d9d364ec7e0965f52a04c2)

Surgical follow-up on the same branch. Author/committer: Jun He freefcw@gmail.com. File: crates/gpui-linux/src/linux/wayland/window.rs only.

Sourcery — compositor-paced retry is now reachable

frame() still sets FrameLoop::Ticking immediately, so complete_frame() never observes FrameLoop::PresentationFailed. The retry policy now keys on presentation state via PresentationState::compositor_paced_retry() (== RetryAfterPresent), not on the frame-loop enum.

  • After a successful present, a later failed renderer.draw stays on RetryAfterPresent and commits a compositor frame callback (AwaitingCallback).
  • Before the first present, or when throttling skipped draw (RetryBeforeFirstPresent), retries still use the 60 Hz timer.
  • draw() still records FrameLoop::PresentationFailed on a failed present; that is no longer the complete_frame gate.

Reachability is covered by failed_present_after_a_successful_frame_uses_compositor_pacing in presentation_state_tests: Presented.failed() is RetryAfterPresent, and that state is the only one for which compositor_paced_retry() is true.

Codex P1 — hidden windows stay unmapped

draw() now returns without attaching a buffer when !state.visible (XDG and configured layer-shell), latching redraw_requested so a later show() can paint. frame() / complete_frame() / schedule_frame() also refuse to present or arm a wakeup while hidden; hide() parks the loop after setting visible = false. Overlay and click-through (set_input_region / set_mouse_passthrough) still commit independently of the present path.

CI hygiene (c209002548c4622800c0dad2268d7c3eda6190d2)

shellcheck scripts/*.sh without -x cannot follow verify-common.sh (SC1091). CI now runs shellcheck --source-path=scripts -x scripts/*.sh.

Files

Demand-driven loop

Font prewarm

  • crates/gpui/src/platform.rsprewarm_fonts trait default
  • crates/gpui/src/text_system.rsTextSystem::prewarm_fonts
  • crates/gpui-linux/src/linux/text_system.rs — cosmic-text cache prewarm + shared FontMatchProperties

CI hygiene

  • .github/workflows/gpui-feature-matrix.yml — follow sourced scripts in shellcheck

Skipped hunks (intentional)

  • crates/gpui_web/src/window.rs — no web backend here
  • crates/zed/src/main.rs theme-font prewarm caller — this is a library; apps call TextSystem::prewarm_fonts
  • event_arena.clear() — this tree has no event arena
  • WindowInvalidator::wake_platform() — not present here
  • Full Zed TestWindow rewrite (simulate_scheduled_frame, frame_callback_pending) — this repo’s test window is a different, simpler harness
  • Three Zed parked-loop #[gpui::test]s that require that harness
  • Wayland IME take/restore in update_ime_enabled — keep this fork’s required_ime_state_change
  • Zed WaylandSurfaceState::Popup / popup reposition via is_configured() — this fork uses LayerShell + WindowKind::Overlay
  • X11, macOS, Windows window.rs; wgpu_renderer.rs; wholesale overwrite of app.rs / window.rs / platform.rs / client.rs

Tests run

  • cargo fmt --all -- --check
  • cargo test -p adabraka-gpui-core --lib --features test-support -- --test-threads=1 — 217 passed (pre-rebase)
  • cargo test -p adabraka-gpui-linux --lib --features test-support -- --test-threads=1 — 40 passed (includes failed_present_after_a_successful_frame_uses_compositor_pacing)
  • cargo clippy -p adabraka-gpui-core -p adabraka-gpui-linux --lib --tests --features test-support -- -D warnings
  • shellcheck --source-path=scripts -x scripts/*.sh — clean

Could not exercise a live Wayland compositor in this environment; the demand-driven loop and hide/show present guards are covered by unit tests and compile, not by a fullscreen compositor repro.

Open in Web Open in Cursor 

Sourcery 摘要

采用需求驱动的 Wayland 渲染和 Linux 字体匹配缓存预热,以改善空闲窗口行为和文本整形响应速度。

新功能:

  • 为 Wayland 窗口添加需求驱动的帧调度,仅在需要渲染工作时唤醒,并在演示被限流或失败时重试。
  • 添加字体缓存预热 API,并支持 Linux cosmic-text 的主要字体和回退字体匹配。

错误修复:

  • 防止空闲的全屏 Wayland 窗口因空的合成器心跳提交而冻结或消耗 CPU。
  • 确保刷新、重绘和延迟的帧回调能够唤醒处于休眠状态的 Wayland 渲染循环。

增强功能:

  • 重构 Linux 字体匹配,使文本整形和缓存预热共享字体属性。

测试:

  • 增加对 Wayland 演示状态转换、异步刷新渲染,以及使用回退字体安全进行字体预热的测试覆盖。
Original summary in English

Sourcery 摘要

通过让 Wayland 帧交付按需进行,并预热字体匹配缓存,提升 Linux 渲染响应速度和文本整形性能。

新功能:

  • 添加按需驱动的 Wayland 帧调度:让空闲窗口进入暂停状态,并在有渲染任务或需要重试时唤醒它们。
  • 添加字体预热 API,并支持在 Linux 上预热主字体和回退字体匹配缓存。

错误修复:

  • 防止空闲的全屏 Wayland 窗口因空的合成器心跳提交而冻结或消耗 CPU。
  • 确保刷新、重绘、延迟回调和呈现失败能够唤醒 Wayland 渲染循环。

增强功能:

  • 集中管理 Linux 字体匹配属性,供文本整形和缓存预热复用。

测试:

  • 增加对 Wayland 呈现状态转换、异步刷新行为以及带回退字体的安全字体预热的测试覆盖。
Original summary in English

Sourcery 总结

通过让 Wayland 帧传递采用需求驱动,并预热字体匹配缓存,改善 Linux 渲染和文本响应能力。

新功能:

  • 为 Wayland 窗口添加需求驱动的帧调度,包括唤醒渲染任务,以及对受限或演示失败情况的重试。
  • 添加字体缓存预热 API,并支持 Linux 上的主字体和回退字体匹配。

错误修复:

  • 通过空的合成器心跳提交,防止处于空闲状态的全屏 Wayland 窗口冻结或消耗 CPU。
  • 确保刷新、重绘、延迟回调和演示失败能够唤醒处于休眠状态的 Wayland 渲染循环。

增强功能:

  • 在文本整形和缓存预热之间共享 Linux 字体匹配属性。

测试:

  • 增加对 Wayland 演示状态转换、异步刷新行为,以及使用回退字体进行安全字体预热的测试覆盖。
Original summary in English

Sourcery 总结

采用按需驱动的 Wayland 渲染和 Linux 字体匹配缓存预热,以改善空闲窗口行为和文本塑形响应速度。

新功能:

  • 添加按需驱动的 Wayland 帧调度,仅在需要渲染工作时唤醒窗口,并重试延迟的呈现操作。
  • 通过 GPUI 文本 API 提供字体缓存预热功能,并支持 Linux 上的主字体和备用字体匹配。

错误修复:

  • 防止空闲的全屏 Wayland 窗口因空的合成器心跳提交而冻结或消耗 CPU。
  • 确保刷新、重绘、延迟回调和呈现失败能够唤醒处于休眠状态的 Wayland 渲染循环。

增强功能:

  • 在文本塑形和缓存预热之间共享 Linux 字体匹配配置。

测试:

  • 增加对 Wayland 呈现状态转换、异步刷新行为以及使用备用字体进行安全字体预热的测试覆盖。
Original summary in English

Sourcery 摘要

通过按需驱动的 Wayland 调度和主动预热字体匹配缓存,提升 Linux 渲染响应速度和空闲行为。

新功能:

  • 添加按需驱动的 Wayland 帧调度:仅在存在待处理的渲染工作时唤醒,并在需要呈现时使用合成器回调或定时重试。
  • 通过 GPUI 文本 API 提供字体缓存预热功能,并支持在 Linux 上预热主字体和备用字体匹配。

错误修复:

  • 防止处于空闲或隐藏状态的 Wayland 窗口冻结、因空提交消耗 CPU,或在未映射时呈现缓冲区。
  • 确保刷新、重绘、延迟帧回调、受限帧以及呈现失败都能恢复已暂停的渲染循环。

增强功能:

  • 在文本整形和缓存预热之间共享 Linux 字体匹配属性。

CI:

  • 修复 Shell 脚本验证,使 ShellCheck 能够跟随被源代码引用的项目脚本。

测试:

  • 增加对 Wayland 呈现重试状态以及使用备用字体安全预热的测试覆盖。
  • 增加用于确认异步刷新增量会触发渲染的测试覆盖。

杂项:

  • 将平台帧完成钩子重命名为 schedule frame,以支持按需驱动的平台。
Original summary in English

Sourcery 摘要

通过按需驱动的 Wayland 调度和预热字体匹配,提升 Linux 渲染响应速度和空闲行为。

新功能:

  • 为 Wayland 窗口添加按需驱动的帧调度,仅在需要进行渲染工作或重试时唤醒。
  • 添加 GPUI 字体缓存预热 API,并通过 Linux cosmic-text 支持主字体和回退字体。

错误修复:

  • 防止空闲或隐藏的 Wayland 窗口冻结、因空提交消耗 CPU,或在未映射时进行呈现。
  • 确保刷新、重绘、帧回调、节流帧以及失败的呈现能够恢复处于暂停状态的 Wayland 渲染循环。

增强功能:

  • 在文本整形和缓存预热之间共享 Linux 字体匹配属性。

CI:

  • 更新 ShellCheck 配置,以便正确分析被源加载的项目脚本。

测试:

  • 增加对 Wayland 呈现重试状态、异步刷新渲染,以及使用回退字体进行安全字体预热的测试覆盖。
Original summary in English

Summary by Sourcery

Improve Linux rendering responsiveness and idle behavior with demand-driven Wayland scheduling and prewarmed font matching.

New Features:

  • Add demand-driven frame scheduling for Wayland windows, waking only when rendering work or a retry is needed.
  • Add a GPUI font-cache prewarming API with Linux cosmic-text support for primary and fallback fonts.

Bug Fixes:

  • Prevent idle or hidden Wayland windows from freezing, consuming CPU through empty commits, or presenting while unmapped.
  • Ensure refreshes, redraws, frame callbacks, throttled frames, and failed presentations can resume the parked Wayland render loop.

Enhancements:

  • Share Linux font-matching properties between text shaping and cache prewarming.

CI:

  • Update ShellCheck configuration so sourced project scripts are analyzed correctly.

Tests:

  • Add coverage for Wayland presentation retry states, asynchronous refresh rendering, and safe font prewarming with fallback fonts.

@sourcery-ai

sourcery-ai Bot commented Aug 29, 2026

Copy link
Copy Markdown

审查者指南

移植了两项针对 Linux GPUI 的修复:Wayland 现在使用需求驱动的渲染循环,使空闲窗口进入休眠状态,并在有待处理工作时显式唤醒或重试;文本系统提供了字体缓存预热功能,Linux cosmic-text 会在字形整形之前预热主字体和回退字体匹配。核心行为已通过单元测试覆盖,但由于当前环境限制,实时合成器行为仍未经测试。

Sequence diagram for demand-driven Wayland frame scheduling

sequenceDiagram
    participant App
    participant Window
    participant Wayland
    participant Compositor

    App->>Window: schedule_frame()
    Window->>Wayland: ping()
    Wayland->>Window: scheduled_frame_fired()
    Window->>App: request_frame_callback
    App->>Window: draw(scene)
    alt presentation succeeds
        Window->>Compositor: surface.frame()
        Window->>Compositor: surface.commit()
        Compositor-->>Window: frame_callback_fired()
    else presentation throttled or fails
        Window->>Wayland: schedule_frame_retry()
        Wayland->>Window: retry_timer_fired()
    end
    Window->>Window: Park when no work remains
Loading

Sequence diagram for Linux font-match cache prewarming

sequenceDiagram
    participant Caller
    participant TextSystem
    participant CosmicText

    Caller->>TextSystem: prewarm_fonts(fonts)
    TextSystem->>TextSystem: resolve_font(font)
    TextSystem->>CosmicText: prewarm_fonts(font_ids)
    loop each primary and fallback font
        CosmicText->>CosmicText: font_match_properties(font_id)
        CosmicText->>CosmicText: get_font_matches(attributes)
    end
    Caller->>TextSystem: shape text without cold font-match lookup
Loading

State diagram for the demand-driven Wayland frame loop

stateDiagram-v2
    [*] --> Unconfigured
    Unconfigured --> Scheduled: schedule_frame()
    Scheduled --> Ticking: scheduled_frame_fired()
    Ticking --> AwaitingCallback: draw() presents
    Ticking --> PresentationFailed: draw() fails
    Ticking --> RetryScheduled: schedule_frame()
    AwaitingCallback --> Ticking: frame_callback_fired()
    PresentationFailed --> RetryScheduled: retry_timer_fired()
    RetryScheduled --> Ticking: retry_timer_fired()
    Ticking --> Parked: no redraw or callback demand
    Parked --> Scheduled: schedule_frame()
    PresentationFailed --> PresentationFailed: draw() fails
    AwaitingCallback --> Parked: callback completes without work
Loading

文件级变更

变更 详细信息 文件
将 Wayland 的心跳渲染替换为需求驱动的帧调度,并显式跟踪呈现状态。
  • 使空闲窗口进入休眠状态,而不是每个 tick 都请求合成器回调。
  • 通过帧 ping 唤醒休眠窗口,以处理重绘、刷新、延迟回调及其他待处理工作。
  • 在呈现成功时使用合成器回调;在呈现受限或失败时使用 60 Hz 定时器进行重试。
  • 使用专用的帧循环状态机跟踪配置、回调、重绘和呈现状态转换。
  • 更新 Wayland 事件和窗口管理路径,在表面状态发生变化时请求重绘。
crates/gpui/src/platform.rs
crates/gpui/src/window.rs
crates/gpui/src/app.rs
crates/gpui/src/app/async_context.rs
crates/gpui-linux/src/linux/wayland/client.rs
crates/gpui-linux/src/linux/wayland/window.rs
添加跨平台字体缓存预热 API,并实现 Linux cosmic-text 匹配预热。
  • 添加默认的平台级和 TextSystem 预热入口,用于解析和去重字体 ID。
  • 提取字形整形和预热共同使用的字体匹配属性。
  • 在 Linux cosmic-text 后端中预热主字体和已配置的回退字体匹配。
  • 安全地跳过不可用的字体数据库条目,并使用已加载字体和回退字体验证预热功能。
crates/gpui/src/platform.rs
crates/gpui/src/text_system.rs
crates/gpui-linux/src/linux/text_system.rs
扩展对休眠循环行为和异步刷新的回归测试覆盖。
  • 测试呈现状态的重试转换。
  • 验证应用进入休眠状态后,异步刷新会触发渲染。
  • 覆盖安全的字体预热,以及随后支持回退字体的布局。
crates/gpui-linux/src/linux/wayland/window.rs
crates/gpui-linux/src/linux/text_system.rs
crates/gpui/src/app.rs

提示和命令

与 Sourcery 交互

  • 触发新的审查: 在拉取请求中评论 @sourcery-ai review
  • 继续讨论: 直接回复 Sourcery 的审查评论。
  • 根据审查评论生成 GitHub issue: 回复审查评论,请 Sourcery 根据该评论创建 issue。你也可以回复审查评论并使用 @sourcery-ai issue,以根据该评论创建 issue。
  • 生成拉取请求标题: 在拉取请求标题的任意位置写入 @sourcery-ai,即可随时生成标题。你也可以在拉取请求中评论 @sourcery-ai title,以随时生成或重新生成标题。
  • 生成拉取请求摘要: 在拉取请求正文的任意位置写入 @sourcery-ai summary,即可在指定位置随时生成 PR 摘要。你也可以在拉取请求中评论 @sourcery-ai summary,以随时生成或重新生成摘要。
  • 生成审查者指南: 在拉取请求中评论 @sourcery-ai guide,即可随时生成或重新生成审查者指南。
  • 解决所有 Sourcery 评论: 在拉取请求中评论 @sourcery-ai resolve,即可解决所有 Sourcery 评论。如果你已经处理完所有评论且不想再看到它们,此功能会很有用。
  • 忽略所有 Sourcery 审查: 在拉取请求中评论 @sourcery-ai dismiss,即可忽略所有现有的 Sourcery 审查。如果你想从新的审查开始,这项功能尤其有用——别忘了评论 @sourcery-ai review 来触发新的审查!

自定义使用体验

访问你的控制面板

  • 启用或禁用审查功能,例如 Sourcery 生成的拉取请求摘要、审查者指南等。
  • 更改审查语言。
  • 添加、删除或编辑自定义审查说明。
  • 调整其他审查设置。

获取帮助

Original review guide in English

Reviewer's Guide

Ports two targeted Linux GPUI fixes: Wayland now uses a demand-driven render loop that parks idle windows and explicitly wakes or retries them when work is pending, while the text system exposes font-cache prewarming and Linux cosmic-text warms primary and fallback matches before shaping. Core behavior is covered by unit tests, but live compositor behavior remains untested in this environment.

Sequence diagram for demand-driven Wayland frame scheduling

sequenceDiagram
    participant App
    participant Window
    participant Wayland
    participant Compositor

    App->>Window: schedule_frame()
    Window->>Wayland: ping()
    Wayland->>Window: scheduled_frame_fired()
    Window->>App: request_frame_callback
    App->>Window: draw(scene)
    alt presentation succeeds
        Window->>Compositor: surface.frame()
        Window->>Compositor: surface.commit()
        Compositor-->>Window: frame_callback_fired()
    else presentation throttled or fails
        Window->>Wayland: schedule_frame_retry()
        Wayland->>Window: retry_timer_fired()
    end
    Window->>Window: Park when no work remains
Loading

Sequence diagram for Linux font-match cache prewarming

sequenceDiagram
    participant Caller
    participant TextSystem
    participant CosmicText

    Caller->>TextSystem: prewarm_fonts(fonts)
    TextSystem->>TextSystem: resolve_font(font)
    TextSystem->>CosmicText: prewarm_fonts(font_ids)
    loop each primary and fallback font
        CosmicText->>CosmicText: font_match_properties(font_id)
        CosmicText->>CosmicText: get_font_matches(attributes)
    end
    Caller->>TextSystem: shape text without cold font-match lookup
Loading

State diagram for the demand-driven Wayland frame loop

stateDiagram-v2
    [*] --> Unconfigured
    Unconfigured --> Scheduled: schedule_frame()
    Scheduled --> Ticking: scheduled_frame_fired()
    Ticking --> AwaitingCallback: draw() presents
    Ticking --> PresentationFailed: draw() fails
    Ticking --> RetryScheduled: schedule_frame()
    AwaitingCallback --> Ticking: frame_callback_fired()
    PresentationFailed --> RetryScheduled: retry_timer_fired()
    RetryScheduled --> Ticking: retry_timer_fired()
    Ticking --> Parked: no redraw or callback demand
    Parked --> Scheduled: schedule_frame()
    PresentationFailed --> PresentationFailed: draw() fails
    AwaitingCallback --> Parked: callback completes without work
Loading

File-Level Changes

Change Details Files
Replace Wayland heartbeat rendering with demand-driven frame scheduling and explicit presentation state.
  • Park idle windows instead of requesting compositor callbacks every tick.
  • Wake parked windows through frame pings for redraw, refresh, deferred callbacks, and other pending work.
  • Use compositor callbacks for successful presentation and 60 Hz timers for throttled or failed presentation retries.
  • Track configuration, callback, redraw, and presentation transitions with a dedicated frame-loop state machine.
  • Update Wayland event and window-management paths to request redraws when surface state changes.
crates/gpui/src/platform.rs
crates/gpui/src/window.rs
crates/gpui/src/app.rs
crates/gpui/src/app/async_context.rs
crates/gpui-linux/src/linux/wayland/client.rs
crates/gpui-linux/src/linux/wayland/window.rs
Add a cross-platform font-cache prewarming API and implement Linux cosmic-text matching prewarm.
  • Add default platform and TextSystem prewarm entry points that resolve and deduplicate font IDs.
  • Extract shared font-match attributes used by both shaping and prewarming.
  • Warm primary and configured fallback font matches in the Linux cosmic-text backend.
  • Safely skip unavailable font database entries and verify prewarming with loaded and fallback fonts.
crates/gpui/src/platform.rs
crates/gpui/src/text_system.rs
crates/gpui-linux/src/linux/text_system.rs
Expand regression coverage for parked-loop behavior and asynchronous refresh.
  • Test presentation-state retry transitions.
  • Verify asynchronous refresh causes a render after the app is parked.
  • Cover safe font prewarming followed by fallback-aware layout.
crates/gpui-linux/src/linux/wayland/window.rs
crates/gpui-linux/src/linux/text_system.rs
crates/gpui/src/app.rs

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@cursor
cursor Bot force-pushed the cursor/wayland-loop-font-cache-a5ca branch from 7d45621 to 94e6008 Compare August 29, 2026 09:40
@freefcw
freefcw marked this pull request as ready for review August 29, 2026 09:40
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 29, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-08-29T09:44:43.149523Z 94e6008 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

嗨——我发现了 1 个问题

面向 AI Agent 的提示
请处理此次代码审查中的评论:

## 单独评论

### 评论 1
<location path="crates/gpui-linux/src/linux/wayland/window.rs" line_range="728-737" />
<code_context>
+            // Before the first present, or when throttling skipped draw, a
+            // callback may never arrive. Otherwise let the compositor pace
+            // retries so an occluded window does not keep polling.
+            if frame_loop == FrameLoop::PresentationFailed
+                && state.presentation == PresentationState::RetryAfterPresent
+            {
+                if state.pending_frame_callback.is_none() {
+                    let callback = state.surface.frame(&state.globals.qh, state.surface.id());
+                    state.pending_frame_callback = Some(callback);
+                }
+                state.surface.commit();
+                self.frame_loop.set(FrameLoop::AwaitingCallback);
+                return;
+            }
+
</code_context>
<issue_to_address>
**问题(bug_risk):** `FrameLoop::PresentationFailed && RetryAfterPresent` 分支无法到达:每次重试都会进入 `frame()`,而该函数首先将循环状态更改为 `Ticking`,因此 `complete_frame()` 永远不会观察到 `FrameLoop::PresentationFailed`。结果是,首次成功呈现之后发生呈现失败时,系统不会为合成器节奏控制提交帧回调,而是不断安排固定的 60 Hz 重试计时器,即使表面被遮挡时也是如此。

**触发条件:** 窗口已经呈现至少一帧后,`renderer.draw(scene)` 返回 false。

**建议修复:** 在将状态更改为 `Ticking` 之前检查之前的帧循环状态,或者让 `complete_frame` 的条件仅依赖呈现状态,以便执行由合成器控制节奏的重试路径。
</issue_to_address>

Sourcery 对开源项目免费——如果您喜欢我们的审查,请考虑分享它们 ✨
帮我变得更有用!请在每条评论上点击 👍 或 👎,我会利用这些反馈来改进审查结果。
Original comment in English

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="crates/gpui-linux/src/linux/wayland/window.rs" line_range="728-737" />
<code_context>
+            // Before the first present, or when throttling skipped draw, a
+            // callback may never arrive. Otherwise let the compositor pace
+            // retries so an occluded window does not keep polling.
+            if frame_loop == FrameLoop::PresentationFailed
+                && state.presentation == PresentationState::RetryAfterPresent
+            {
+                if state.pending_frame_callback.is_none() {
+                    let callback = state.surface.frame(&state.globals.qh, state.surface.id());
+                    state.pending_frame_callback = Some(callback);
+                }
+                state.surface.commit();
+                self.frame_loop.set(FrameLoop::AwaitingCallback);
+                return;
+            }
+
</code_context>
<issue_to_address>
**issue (bug_risk):** The `FrameLoop::PresentationFailed && RetryAfterPresent` branch is unreachable: every retry enters `frame()`, which first changes the loop state to `Ticking`, so `complete_frame()` never observes `FrameLoop::PresentationFailed`. As a result, a failed presentation after the first successful presentation never commits a frame callback for compositor pacing and instead repeatedly schedules the fixed 60 Hz retry timer, including while the surface is occluded.

**Triggers:** When `renderer.draw(scene)` returns false after the window has already presented at least one frame.

**Suggested fix:** Check the previous frame-loop state before changing it to `Ticking`, or make the `complete_frame` condition depend only on the presentation state so the compositor-paced retry path can execute.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread crates/gpui-linux/src/linux/wayland/window.rs Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 94e6008579

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1576 to +1579
if is_unconfigured_layer_shell(&state.role) {
state.redraw_requested = true;
return;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Prevent hidden Wayland windows from drawing

When hide() has set visible = false and detached the surface buffer, a normal entity or application refresh can now schedule this window again. Because this guard only excludes unconfigured layer-shell surfaces, both hidden XDG windows and configured layer-shell windows proceed to renderer.draw, whose presentation attaches a buffer and maps the supposedly hidden window again. Check state.visible here or suppress frame scheduling while the window is hidden.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 07fda8e.

draw() now returns without attaching a buffer when !state.visible (XDG and configured layer-shell), latching redraw_requested so a later show() can paint. frame() / complete_frame() / schedule_frame() also refuse to present or arm a wakeup while hidden; hide() parks the loop after setting visible = false. Overlay and click-through (set_input_region / set_mouse_passthrough) still commit independently of the present path.

@cursor
cursor Bot force-pushed the cursor/wayland-loop-font-cache-a5ca branch from c8d86d6 to ee15530 Compare August 29, 2026 12:06
Wayland frame callbacks only fire after a commit the compositor paints.
Park the loop when idle instead of committing empty heartbeat frames,
and wake from GPUI via schedule_frame when a window still needs work.

Zed-Origin: eb354c8d504071bdb79110a7a5c9d374c2864113

Co-authored-by: freefcw <freefcw@gmail.com>
Expose TextSystem::prewarm_fonts and warm cosmic-text get_font_matches
for the requested fonts so shaping does not pay that cost on the hot path.

Zed-Origin: f1d27d545e79f98ffde7ebf3229f8eb8bad791aa

Co-authored-by: freefcw <freefcw@gmail.com>
Key compositor-paced retries on PresentationState::RetryAfterPresent so
frame() setting Ticking no longer skips the occluded-surface path.
Skip buffer attach and frame scheduling while visible is false so a
refresh cannot remap a hidden XDG or configured layer-shell window.

Co-authored-by: freefcw <freefcw@gmail.com>
shellcheck without -x cannot follow verify-common.sh, and the first
release-archive loop never uses manifest_path. Point shellcheck at
scripts/ and ignore the unused TSV column.

Co-authored-by: freefcw <freefcw@gmail.com>
@cursor
cursor Bot force-pushed the cursor/wayland-loop-font-cache-a5ca branch from ee15530 to c209002 Compare August 29, 2026 13:14
@freefcw
freefcw merged commit 1e1d854 into develop/0.9 Aug 29, 2026
19 checks passed
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.

1 participant