mirror of
https://github.com/stablyai/orca.git
synced 2026-10-17 00:03:08 +00:00
Gate WebGL context retention to Windows
Context re-creation is ~5ms on macOS/Linux GL, so retention buys no perceptible switch latency there while still costing hidden-pane GPU memory and a new lifecycle to reason about. Only Windows (ANGLE -> D3D11, 100-500ms per pane) retains; other platforms keep the long-proven dispose-on-hide behavior.
This commit is contained in:
@@ -7,7 +7,11 @@ import {
|
||||
markComplexScriptOutput,
|
||||
resetWebglTextureAtlas
|
||||
} from './pane-webgl-renderer'
|
||||
import { retainSuspendedWebglPane, unretainWebglPane } from './pane-webgl-context-retention'
|
||||
import {
|
||||
retainSuspendedWebglPane,
|
||||
shouldRetainSuspendedWebglContexts,
|
||||
unretainWebglPane
|
||||
} from './pane-webgl-context-retention'
|
||||
import { reattachWebglIfNeeded } from './pane-webgl-reattach'
|
||||
|
||||
export function setPaneGpuRenderingState(
|
||||
@@ -44,12 +48,17 @@ export function markPaneComplexScriptOutput(
|
||||
}
|
||||
|
||||
export function suspendPaneRendering(panes: Iterable<ManagedPaneInternal>): void {
|
||||
const retainContexts = shouldRetainSuspendedWebglContexts()
|
||||
for (const pane of panes) {
|
||||
// Why: deferred blocks NEW context creation while hidden; live contexts
|
||||
// are retained (not disposed) so the return switch repaints instantly
|
||||
// instead of paying ANGLE context re-creation. The LRU cap bounds how
|
||||
// many hidden panes may keep one.
|
||||
// Why: deferred blocks NEW context creation while hidden; on Windows,
|
||||
// live contexts are retained (not disposed) so the return switch repaints
|
||||
// instantly instead of paying ANGLE context re-creation. The LRU cap
|
||||
// bounds how many hidden panes may keep one.
|
||||
pane.webglAttachmentDeferred = true
|
||||
if (!retainContexts) {
|
||||
disposeWebgl(pane)
|
||||
continue
|
||||
}
|
||||
for (const evicted of retainSuspendedWebglPane(pane)) {
|
||||
disposeWebgl(evicted)
|
||||
}
|
||||
|
||||
@@ -66,16 +66,21 @@ function fireContextLoss(pane: ManagedPaneInternal): void {
|
||||
addon._onContextLoss.fire()
|
||||
}
|
||||
|
||||
describe('terminal WebGL context retention across hide/show', () => {
|
||||
function stubCommonGlobals(userAgent: string): void {
|
||||
vi.stubGlobal('navigator', { userAgent })
|
||||
vi.stubGlobal('requestAnimationFrame', (callback: FrameRequestCallback) => {
|
||||
callback(16)
|
||||
return 1
|
||||
})
|
||||
vi.stubGlobal('cancelAnimationFrame', vi.fn())
|
||||
}
|
||||
|
||||
describe('terminal WebGL context retention across hide/show (Windows)', () => {
|
||||
beforeEach(() => {
|
||||
resetTerminalWebglSuggestion()
|
||||
clearRetainedWebglPanes()
|
||||
vi.spyOn(console, 'warn').mockImplementation(() => {})
|
||||
vi.stubGlobal('requestAnimationFrame', (callback: FrameRequestCallback) => {
|
||||
callback(16)
|
||||
return 1
|
||||
})
|
||||
vi.stubGlobal('cancelAnimationFrame', vi.fn())
|
||||
stubCommonGlobals('Mozilla/5.0 (Windows NT 10.0; Win64; x64)')
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
@@ -193,3 +198,38 @@ describe('terminal WebGL context retention across hide/show', () => {
|
||||
expect(pane.webglAddon).not.toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('terminal WebGL context retention is Windows-only', () => {
|
||||
beforeEach(() => {
|
||||
resetTerminalWebglSuggestion()
|
||||
clearRetainedWebglPanes()
|
||||
vi.spyOn(console, 'warn').mockImplementation(() => {})
|
||||
stubCommonGlobals('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)')
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
clearRetainedWebglPanes()
|
||||
vi.unstubAllGlobals()
|
||||
vi.restoreAllMocks()
|
||||
})
|
||||
|
||||
it('keeps the dispose-on-hide behavior off Windows', () => {
|
||||
const pane = createAttachedPane()
|
||||
|
||||
suspendPaneRendering([pane])
|
||||
|
||||
expect(pane.webglAttachmentDeferred).toBe(true)
|
||||
expect(pane.webglAddon).toBeNull()
|
||||
expect(retainedWebglPaneCount()).toBe(0)
|
||||
})
|
||||
|
||||
it('re-attaches on resume off Windows as before', () => {
|
||||
const pane = createAttachedPane()
|
||||
suspendPaneRendering([pane])
|
||||
|
||||
resumePaneRendering([pane])
|
||||
|
||||
expect(pane.webglAddon).not.toBeNull()
|
||||
expect(pane.terminal.loadAddon).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { getRendererAppPlatform } from '@/lib/renderer-app-platform'
|
||||
import type { ManagedPaneInternal } from './pane-manager-types'
|
||||
|
||||
// Why: workspace switches used to dispose every hidden pane's WebGL context
|
||||
@@ -7,6 +8,13 @@ import type { ManagedPaneInternal } from './pane-manager-types'
|
||||
// this LRU cap only bounds hidden-pane GPU memory.
|
||||
export const RETAINED_WEBGL_PANE_CAP = 16
|
||||
|
||||
/** Windows-only: context re-creation is cheap on macOS/Linux GL, so hidden
|
||||
* panes there keep the long-proven dispose-on-hide behavior instead of
|
||||
* paying retention's GPU-memory cost for no perceptible switch latency win. */
|
||||
export function shouldRetainSuspendedWebglContexts(): boolean {
|
||||
return getRendererAppPlatform() === 'win32'
|
||||
}
|
||||
|
||||
// Insertion order doubles as LRU order: re-retaining deletes + re-adds.
|
||||
const retainedPanes = new Set<ManagedPaneInternal>()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user