fix: reduce macOS popup compositing cost
This commit is contained in:
@@ -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.
|
||||||
@@ -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.
|
||||||
@@ -10,6 +10,11 @@ import './styles/globals.css';
|
|||||||
import 'katex/dist/katex.min.css';
|
import 'katex/dist/katex.min.css';
|
||||||
import { initializeDefaultTransports } from './lib/api-client';
|
import { initializeDefaultTransports } from './lib/api-client';
|
||||||
|
|
||||||
|
const platform = window.electron?.platform;
|
||||||
|
if (platform) {
|
||||||
|
document.documentElement.dataset.platform = platform;
|
||||||
|
}
|
||||||
|
|
||||||
initializeDefaultTransports();
|
initializeDefaultTransports();
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||||
|
|||||||
@@ -219,6 +219,48 @@
|
|||||||
backdrop-filter: blur(18px) saturate(135%);
|
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
|
/* 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
|
* 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
|
* the transparent native window material, so the light surface needs to stay
|
||||||
|
|||||||
62
tests/e2e/popup-performance.spec.ts
Normal file
62
tests/e2e/popup-performance.spec.ts
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import { expect, getStableWindow, test } from './fixtures/electron';
|
||||||
|
|
||||||
|
type PopupStyles = {
|
||||||
|
contentBackdropFilter: string;
|
||||||
|
overlayBackdropFilter: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
async function readPopupStyles(page: Awaited<ReturnType<typeof getStableWindow>>): Promise<PopupStyles> {
|
||||||
|
return await page.evaluate(() => {
|
||||||
|
const content = document.querySelector<HTMLElement>('[role="dialog"]');
|
||||||
|
const overlay = [...document.querySelectorAll<HTMLElement>('.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());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user