From 0f761f33b3fc9e2ecb0aae2ac015b60d3ebb2cdf Mon Sep 17 00:00:00 2001 From: brother7 <7brother7@gmail.com> Date: Tue, 18 Aug 2026 02:30:05 +0800 Subject: [PATCH] fix: reduce macOS popup compositing cost --- .../tasks/20260818-macos-popup-lag-7c4e.md | 76 +++++++++++++++++++ ...macos-popup-lag-7c4e__popup-compositing.md | 48 ++++++++++++ src/main.tsx | 5 ++ src/styles/globals.css | 42 ++++++++++ tests/e2e/popup-performance.spec.ts | 62 +++++++++++++++ 5 files changed, 233 insertions(+) create mode 100644 .project-docs/30-worklog/tasks/20260818-macos-popup-lag-7c4e.md create mode 100644 .project-docs/50-evidence/topics/20260818-macos-popup-lag-7c4e__popup-compositing.md create mode 100644 tests/e2e/popup-performance.spec.ts diff --git a/.project-docs/30-worklog/tasks/20260818-macos-popup-lag-7c4e.md b/.project-docs/30-worklog/tasks/20260818-macos-popup-lag-7c4e.md new file mode 100644 index 0000000..c7fb414 --- /dev/null +++ b/.project-docs/30-worklog/tasks/20260818-macos-popup-lag-7c4e.md @@ -0,0 +1,76 @@ +# Task: Diagnose macOS popup lag + +## Identity + +- Task ID: 20260818-macos-popup-lag-7c4e +- Mode: Feature +- Branch: codex/20260818-macos-popup-lag-7c4e-macos-popup-lag +- Worktree: D:\Datas\OthersProjects\makelore-macos-popup-lag +- Base commit: 13bdc0c9bab656aa9ff6c990bf6359f7d3210750 +- Owner: developer +- Status: Completed + +## Scope + +- Diagnose the reported macOS-wide popup lag in the Electron client, with emphasis on the shared Renderer Dialog/Sheet and transient popup compositing path. +- Build a repeatable Electron E2E frame-budget/computed-style probe, minimize it to the shared popup seam, implement the smallest platform-safe rendering fix, and add a regression check. +- Do not change firmware, cloud contracts, authentication, provider/runtime behavior, or the unowned primary worktree. + +## Intent And Constraints + +- Preserve the single Makelore light visual system and existing popup behavior, focus management, accessibility, and Main-owned boundaries. +- The available environment is Windows, so macOS behavior must be simulated through the exposed Electron platform marker; real signed macOS physical/package validation remains unverified. +- Keep the existing global hardware-acceleration policy unchanged unless a reproducible seam proves it is the cause; prefer removing only the expensive transient popup filter path. +- The shared popup regression owns only its test file and the platform/compositing files assigned during implementation; unrelated peer work remains isolated. + +## Outcome + +- Diagnosis isolated the shared Renderer popup compositing seam: the real Settings + Dialog rendered `backdrop-filter: blur(3px)` on its overlay and + `blur(18px) saturate(1.35)` on its glass surface. A repeatable probe observed a + 62.5 ms maximum frame gap and one frame over 32 ms before the filter-only + intervention; disabling those filters reduced the observed gap to about 31.4 + ms with no frame over 32 ms in this environment. +- Implemented a platform-scoped fix. Renderer startup writes + `html[data-platform]` from the existing Electron platform value. On Darwin, + transient popup roots and their `glass-surface` descendants disable both + standard and WebKit backdrop filters and use an opaque background. Win32 and + other platforms retain the existing glass material. +- Main/IPC, hardware-acceleration policy, native vibrancy, and unrelated popup + behavior were left unchanged. The fix is limited to the assigned Renderer + platform/style seam and its regression test. + +## Verification + +- `corepack pnpm@10.33.4 run build:vite` — passed (existing chunk-size and + dynamic-import warnings only). +- `corepack pnpm@10.33.4 run typecheck` — passed. +- `corepack pnpm@10.33.4 exec eslint src/main.tsx tests/e2e/popup-performance.spec.ts` + — passed. +- `corepack pnpm@10.33.4 exec vitest run tests/unit/modal-layering.test.tsx` — + passed (2 tests). +- `corepack pnpm@10.33.4 test` — passed (176 files, 2,109 tests). +- `corepack pnpm@10.33.4 exec node ./node_modules/@playwright/test/cli.js test + tests/e2e/popup-performance.spec.ts --reporter=line` — passed (1 test). +- `git diff --check` — passed. +- The E2E regression confirms the actual Electron platform marker, Darwin + computed filters are `none`, and non-Darwin computed filters remain enabled. + It intentionally does not enforce a hard RAF budget because shared CI + scheduling is not a stable compositor benchmark. +- Real signed macOS package/physical GPU-vibrancy A/B validation was not + available on this Windows host and remains required before claiming measured + macOS frame-time improvement. + +## Follow-ups + +- Run the popup E2E and a short Performance trace against a signed macOS x64 and + arm64 package, including a Sheet and nested Dialog path. +- If popups remain slow with computed filters disabled, A/B the existing + `app.disableHardwareAcceleration()` and Darwin transparent/vibrancy window + material on real hardware before changing either global policy. + +## Promotion Candidates + +- No canonical architecture or product decision is proposed. Keep the + platform-scoped rendering rule and the evidence topic task-scoped until real + macOS measurements confirm the compositor result. diff --git a/.project-docs/50-evidence/topics/20260818-macos-popup-lag-7c4e__popup-compositing.md b/.project-docs/50-evidence/topics/20260818-macos-popup-lag-7c4e__popup-compositing.md new file mode 100644 index 0000000..585d6f2 --- /dev/null +++ b/.project-docs/50-evidence/topics/20260818-macos-popup-lag-7c4e__popup-compositing.md @@ -0,0 +1,48 @@ +# macOS Popup Compositing Probe + +## Claim + +The reported popup lag has a reproducible Renderer-side hotspot in transient +backdrop sampling. The shared Dialog overlay and glass content both applied +`backdrop-filter` in the baseline build. A Darwin-scoped rule now removes those +filters and makes the popup glass surface opaque while leaving other platforms +unchanged. + +## Evidence + +- Environment: Windows development host; the Electron E2E launches the real + shared Settings Dialog, but Darwin is simulated through the exposed platform + marker. A signed macOS package was not available for this run. +- Baseline probe (before the fix, repeated twice): overlay computed + `backdropFilter: blur(3px)`; content computed + `backdropFilter: blur(18px) saturate(1.35)`; maximum sampled frame gap was + 62.5 ms with one frame over 32 ms. +- Single-variable filter probe: both computed filters became `none`; the same + probe observed an approximately 31.4 ms maximum gap and zero frames over + 32 ms in this environment. +- A temporary skip-GPU-call probe did not change the sampled result on this + non-macOS host, so it does not establish or disprove the real macOS + hardware-acceleration hypothesis. + +## Implementation + +- `src/main.tsx` writes `html[data-platform]` before the first React render. +- `src/styles/globals.css` disables both `backdrop-filter` implementations for + Darwin transient popup roots (`dialog`, `alertdialog`, `menu`, `listbox`, + `tooltip`, and full-screen fixed overlays), and uses an opaque background for + their glass surfaces. +- `tests/e2e/popup-performance.spec.ts` checks the real platform marker, Darwin + filter removal, and non-Darwin preservation. It does not impose a fixed RAF + threshold because CI scheduling is not a stable performance baseline. + +## Verification + +`build:vite`, `typecheck`, focused ESLint, focused modal unit tests, the popup +E2E (1/1), and `git diff --check` passed. Existing build warnings are unrelated +chunk-size and dynamic-import warnings. + +## Remaining uncertainty + +Real macOS GPU/vibrancy composition, Sheet coverage, nested Dialog coverage, and +physical frame-time improvement still require signed-package validation on +macOS hardware. diff --git a/src/main.tsx b/src/main.tsx index cd5e83d..71d69c2 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -10,6 +10,11 @@ import './styles/globals.css'; import 'katex/dist/katex.min.css'; import { initializeDefaultTransports } from './lib/api-client'; +const platform = window.electron?.platform; +if (platform) { + document.documentElement.dataset.platform = platform; +} + initializeDefaultTransports(); ReactDOM.createRoot(document.getElementById('root')!).render( diff --git a/src/styles/globals.css b/src/styles/globals.css index 622925d..1da7010 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -219,6 +219,48 @@ backdrop-filter: blur(18px) saturate(135%); } +/* Chromium's backdrop sampling is disproportionately expensive for transient + * layers in macOS Electron windows. Keep the optimization platform-scoped so + * the shared materials remain unchanged elsewhere. */ +html[data-platform='darwin'] :is( + [role='dialog'], + [role='alertdialog'], + [role='menu'], + [role='listbox'], + [role='tooltip'], + .fixed.inset-0 +), +html[data-platform='darwin'] :is( + [role='dialog'], + [role='alertdialog'], + [role='menu'], + [role='listbox'], + [role='tooltip'], + .fixed.inset-0 +) .glass-surface { + -webkit-backdrop-filter: none !important; + backdrop-filter: none !important; +} + +html[data-platform='darwin'] :is( + [role='dialog'], + [role='alertdialog'], + [role='menu'], + [role='listbox'], + [role='tooltip'], + .fixed.inset-0 +).glass-surface, +html[data-platform='darwin'] :is( + [role='dialog'], + [role='alertdialog'], + [role='menu'], + [role='listbox'], + [role='tooltip'], + .fixed.inset-0 +) .glass-surface { + background-color: hsl(var(--background)); +} + /* Sidebar material: keep the rail visibly white while allowing a restrained * amount of the canvas to show through. A low white alpha reads as gray over * the transparent native window material, so the light surface needs to stay diff --git a/tests/e2e/popup-performance.spec.ts b/tests/e2e/popup-performance.spec.ts new file mode 100644 index 0000000..494c5cf --- /dev/null +++ b/tests/e2e/popup-performance.spec.ts @@ -0,0 +1,62 @@ +import { expect, getStableWindow, test } from './fixtures/electron'; + +type PopupStyles = { + contentBackdropFilter: string; + overlayBackdropFilter: string; +}; + +async function readPopupStyles(page: Awaited>): Promise { + return await page.evaluate(() => { + const content = document.querySelector('[role="dialog"]'); + const overlay = [...document.querySelectorAll('.fixed.inset-0')] + .find((element) => element.getAttribute('role') !== 'dialog'); + if (!overlay || !content) { + throw new Error('Expected the open dialog overlay and content'); + } + + const contentStyle = getComputedStyle(content); + const overlayStyle = getComputedStyle(overlay); + return { + contentBackdropFilter: contentStyle.backdropFilter, + overlayBackdropFilter: overlayStyle.backdropFilter, + }; + }); +} + +test.describe('shared popup rendering cost', () => { + test('disables transient popup backdrop filters only on macOS', async ({ launchElectronApp }) => { + const app = await launchElectronApp({ skipSetup: true }); + + try { + const page = await getStableWindow(app); + const actualPlatform = await page.evaluate(() => window.electron.platform); + await expect(page.locator('html')).toHaveAttribute('data-platform', actualPlatform); + + await expect(page.getByTestId('ai-module-selection-page')).toBeVisible(); + await page.getByTestId('ai-module-option-programming').click(); + await expect(page.getByTestId('main-layout')).toBeVisible(); + await page.getByTestId('sidebar-member-menu-trigger').click(); + await page.getByTestId('sidebar-nav-settings').click(); + await expect(page.getByTestId('settings-page')).toBeVisible(); + await page.getByTestId('settings-reveal-additional').click(); + await expect(page.getByRole('dialog')).toBeVisible(); + + await page.evaluate(() => { + document.documentElement.dataset.platform = 'darwin'; + }); + await expect.poll(() => readPopupStyles(page)).toEqual({ + contentBackdropFilter: 'none', + overlayBackdropFilter: 'none', + }); + + await page.evaluate(() => { + document.documentElement.dataset.platform = 'win32'; + }); + const nonMacStyles = await readPopupStyles(page); + expect(nonMacStyles.contentBackdropFilter).not.toBe('none'); + expect(nonMacStyles.overlayBackdropFilter).not.toBe('none'); + } finally { + await app.evaluate(({ app: electronApp }) => electronApp.quit()); + } + }); +});