diff --git a/packages/engine/src/services/browserManager.test.ts b/packages/engine/src/services/browserManager.test.ts index 1f92aaf6b..76c91f41f 100644 --- a/packages/engine/src/services/browserManager.test.ts +++ b/packages/engine/src/services/browserManager.test.ts @@ -14,6 +14,15 @@ import { resolveBrowserGpuMode, } from "./browserManager.js"; +vi.mock("./systemMemory.js", async (importOriginal) => { + const actual = await importOriginal(); + return { ...actual, getSystemTotalMb: vi.fn(() => 32768) }; +}); + +import { getSystemTotalMb, LOW_MEMORY_TOTAL_MB_THRESHOLD } from "./systemMemory.js"; + +const mockGetSystemTotalMb = vi.mocked(getSystemTotalMb); + describe("buildChromeArgs browser GPU mode", () => { const base = { width: 1920, height: 1080 }; @@ -308,3 +317,78 @@ describe("browser pool", () => { await acquirePromise.catch(() => {}); }); }); + +describe("memory-adaptive Chrome flags", () => { + const base = { width: 1920, height: 1080 }; + + beforeEach(() => { + mockGetSystemTotalMb.mockReturnValue(32768); + }); + + afterEach(() => { + mockGetSystemTotalMb.mockReturnValue(32768); + }); + + function heapFlag(args: string[]): number | null { + const flag = args.find((a) => a.includes("--max-old-space-size=")); + if (!flag) return null; + return parseInt(flag.match(/--max-old-space-size=(\d+)/)?.[1] ?? "", 10); + } + + function gpuBudget(args: string[]): number { + const flag = args.find((a) => a.startsWith("--force-gpu-mem-available-mb="))!; + return parseInt(flag.split("=")[1]!, 10); + } + + it("does not set heap limit above threshold", () => { + mockGetSystemTotalMb.mockReturnValue(LOW_MEMORY_TOTAL_MB_THRESHOLD + 1); + expect(heapFlag(buildChromeArgs(base))).toBeNull(); + }); + + it("scales heap to total/8 at threshold", () => { + mockGetSystemTotalMb.mockReturnValue(LOW_MEMORY_TOTAL_MB_THRESHOLD); + expect(heapFlag(buildChromeArgs(base))).toBe(Math.floor(LOW_MEMORY_TOTAL_MB_THRESHOLD / 8)); + }); + + it("scales heap to total/8 on 6 GB systems", () => { + mockGetSystemTotalMb.mockReturnValue(6144); + expect(heapFlag(buildChromeArgs(base))).toBe(768); + }); + + it("uses proportional heap at 4 GB boundary", () => { + mockGetSystemTotalMb.mockReturnValue(4096); + expect(heapFlag(buildChromeArgs(base))).toBe(512); + }); + + it("uses 256 MB heap floor below 4 GB", () => { + mockGetSystemTotalMb.mockReturnValue(3072); + expect(heapFlag(buildChromeArgs(base))).toBe(256); + }); + + it("scales GPU budget to total/4 at threshold", () => { + mockGetSystemTotalMb.mockReturnValue(LOW_MEMORY_TOTAL_MB_THRESHOLD); + expect(gpuBudget(buildChromeArgs(base))).toBe(Math.floor(LOW_MEMORY_TOTAL_MB_THRESHOLD / 4)); + }); + + it("uses proportional GPU budget at 4 GB boundary", () => { + mockGetSystemTotalMb.mockReturnValue(4096); + expect(gpuBudget(buildChromeArgs(base))).toBe(1024); + }); + + it("switches to total/2 GPU and no heap limit just above threshold", () => { + const above = LOW_MEMORY_TOTAL_MB_THRESHOLD + 1; + mockGetSystemTotalMb.mockReturnValue(above); + expect(gpuBudget(buildChromeArgs(base))).toBe(Math.floor(above / 2)); + expect(heapFlag(buildChromeArgs(base))).toBeNull(); + }); + + it("scales GPU budget to total/2 well above threshold", () => { + mockGetSystemTotalMb.mockReturnValue(LOW_MEMORY_TOTAL_MB_THRESHOLD * 2); + expect(gpuBudget(buildChromeArgs(base))).toBe(LOW_MEMORY_TOTAL_MB_THRESHOLD); + }); + + it("uses 512 MB GPU budget below 4 GB", () => { + mockGetSystemTotalMb.mockReturnValue(3072); + expect(gpuBudget(buildChromeArgs(base))).toBe(512); + }); +}); diff --git a/packages/engine/src/services/browserManager.ts b/packages/engine/src/services/browserManager.ts index 48648a5c0..a5d6eb482 100644 --- a/packages/engine/src/services/browserManager.ts +++ b/packages/engine/src/services/browserManager.ts @@ -512,14 +512,14 @@ function getGpuMemBudgetMb(): number { const total = getSystemTotalMb(); if (total < 4096) return 512; - if (total <= LOW_MEMORY_TOTAL_MB_THRESHOLD) return 1024; + if (total <= LOW_MEMORY_TOTAL_MB_THRESHOLD) return Math.floor(total / 4); return Math.min(Math.floor(total / 2), 16384); } function getLowMemoryFlags(): string[] { const total = getSystemTotalMb(); if (total > LOW_MEMORY_TOTAL_MB_THRESHOLD) return []; - const heapMb = total < 4096 ? 256 : 512; + const heapMb = total < 4096 ? 256 : Math.floor(total / 8); return [`--js-flags=--max-old-space-size=${heapMb}`]; }