From 54608bf38d1d7e9d067d9700a9dcd536a48473e5 Mon Sep 17 00:00:00 2001 From: jeanp413 Date: Tue, 12 Dec 2023 04:16:28 -0500 Subject: [PATCH 1/6] Fixes https://github.com/microsoft/vscode/issues/200469 --- src/browser/RenderDebouncer.ts | 11 ++++++----- src/browser/services/RenderService.ts | 5 ++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/browser/RenderDebouncer.ts b/src/browser/RenderDebouncer.ts index b3118d5f..4877a140 100644 --- a/src/browser/RenderDebouncer.ts +++ b/src/browser/RenderDebouncer.ts @@ -4,6 +4,7 @@ */ import { IRenderDebouncerWithCallback } from 'browser/Types'; +import { ICoreBrowserService } from 'browser/services/Services'; /** * Debounces calls to render terminal rows using animation frames. @@ -16,14 +17,14 @@ export class RenderDebouncer implements IRenderDebouncerWithCallback { private _refreshCallbacks: FrameRequestCallback[] = []; constructor( - private _parentWindow: Window, - private _renderCallback: (start: number, end: number) => void + private _renderCallback: (start: number, end: number) => void, + private readonly _coreBrowserService: ICoreBrowserService, ) { } public dispose(): void { if (this._animationFrame) { - this._parentWindow.cancelAnimationFrame(this._animationFrame); + this._coreBrowserService.window.cancelAnimationFrame(this._animationFrame); this._animationFrame = undefined; } } @@ -31,7 +32,7 @@ export class RenderDebouncer implements IRenderDebouncerWithCallback { public addRefreshCallback(callback: FrameRequestCallback): number { this._refreshCallbacks.push(callback); if (!this._animationFrame) { - this._animationFrame = this._parentWindow.requestAnimationFrame(() => this._innerRefresh()); + this._animationFrame = this._coreBrowserService.window.requestAnimationFrame(() => this._innerRefresh()); } return this._animationFrame; } @@ -49,7 +50,7 @@ export class RenderDebouncer implements IRenderDebouncerWithCallback { return; } - this._animationFrame = this._parentWindow.requestAnimationFrame(() => this._innerRefresh()); + this._animationFrame = this._coreBrowserService.window.requestAnimationFrame(() => this._innerRefresh()); } private _innerRefresh(): void { diff --git a/src/browser/services/RenderService.ts b/src/browser/services/RenderService.ts index 9fa8d234..dddd5185 100644 --- a/src/browser/services/RenderService.ts +++ b/src/browser/services/RenderService.ts @@ -10,7 +10,7 @@ import { ICharSizeService, ICoreBrowserService, IRenderService, IThemeService } import { EventEmitter } from 'common/EventEmitter'; import { Disposable, MutableDisposable } from 'common/Lifecycle'; import { DebouncedIdleTask } from 'common/TaskQueue'; -import { IBufferService, IDecorationService, IInstantiationService, IOptionsService } from 'common/services/Services'; +import { IBufferService, IDecorationService, IOptionsService } from 'common/services/Services'; interface ISelectionState { start: [number, number] | undefined; @@ -56,12 +56,11 @@ export class RenderService extends Disposable implements IRenderService { @IDecorationService decorationService: IDecorationService, @IBufferService bufferService: IBufferService, @ICoreBrowserService coreBrowserService: ICoreBrowserService, - @IInstantiationService instantiationService: IInstantiationService, @IThemeService themeService: IThemeService ) { super(); - this._renderDebouncer = new RenderDebouncer(coreBrowserService.window, (start, end) => this._renderRows(start, end)); + this._renderDebouncer = new RenderDebouncer((start, end) => this._renderRows(start, end), coreBrowserService); this.register(this._renderDebouncer); this.register(coreBrowserService.onDprChange(() => this.handleDevicePixelRatioChange())); From 97750f672f2446974c70a3ad9efe3de7fd779f2f Mon Sep 17 00:00:00 2001 From: jeanp413 Date: Tue, 12 Dec 2023 04:41:20 -0500 Subject: [PATCH 2/6] :lipstick: --- src/browser/RenderDebouncer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/browser/RenderDebouncer.ts b/src/browser/RenderDebouncer.ts index 4877a140..dd3b97a6 100644 --- a/src/browser/RenderDebouncer.ts +++ b/src/browser/RenderDebouncer.ts @@ -18,7 +18,7 @@ export class RenderDebouncer implements IRenderDebouncerWithCallback { constructor( private _renderCallback: (start: number, end: number) => void, - private readonly _coreBrowserService: ICoreBrowserService, + private readonly _coreBrowserService: ICoreBrowserService ) { } From 2592e1e84d174d060a7b81243dfe70355bda78db Mon Sep 17 00:00:00 2001 From: jeanp413 Date: Tue, 12 Dec 2023 05:55:30 -0500 Subject: [PATCH 3/6] re register intersection observer on window change --- src/browser/services/RenderService.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/browser/services/RenderService.ts b/src/browser/services/RenderService.ts index dddd5185..f1d25fd2 100644 --- a/src/browser/services/RenderService.ts +++ b/src/browser/services/RenderService.ts @@ -3,12 +3,13 @@ * @license MIT */ +import { IDisposable } from 'common/Types'; import { RenderDebouncer } from 'browser/RenderDebouncer'; import { IRenderDebouncerWithCallback } from 'browser/Types'; import { IRenderDimensions, IRenderer } from 'browser/renderer/shared/Types'; import { ICharSizeService, ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services'; import { EventEmitter } from 'common/EventEmitter'; -import { Disposable, MutableDisposable } from 'common/Lifecycle'; +import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle'; import { DebouncedIdleTask } from 'common/TaskQueue'; import { IBufferService, IDecorationService, IOptionsService } from 'common/services/Services'; @@ -24,6 +25,7 @@ export class RenderService extends Disposable implements IRenderService { private _renderer: MutableDisposable = this.register(new MutableDisposable()); private _renderDebouncer: IRenderDebouncerWithCallback; private _pausedResizeTask = new DebouncedIdleTask(); + private _observerDisposable: IDisposable | undefined; private _isPaused: boolean = false; private _needsFullRefresh: boolean = false; @@ -38,7 +40,7 @@ export class RenderService extends Disposable implements IRenderService { }; private readonly _onDimensionsChange = this.register(new EventEmitter()); - public readonly onDimensionsChange = this._onDimensionsChange.event; + public readonly onDimensionsChange = this._onDimensionsChange.event; private readonly _onRenderedViewportChange = this.register(new EventEmitter<{ start: number, end: number }>()); public readonly onRenderedViewportChange = this._onRenderedViewportChange.event; private readonly _onRender = this.register(new EventEmitter<{ start: number, end: number }>()); @@ -101,12 +103,18 @@ export class RenderService extends Disposable implements IRenderService { this.register(themeService.onChangeColors(() => this._fullRefresh())); + this._registerIntersectionObserver(coreBrowserService.window, screenElement); + this.register(coreBrowserService.onWindowChange((w) => this._registerIntersectionObserver(w, screenElement))); + } + + private _registerIntersectionObserver(w: Window & typeof globalThis, screenElement: HTMLElement): void { // Detect whether IntersectionObserver is detected and enable renderer pause // and resume based on terminal visibility if so - if ('IntersectionObserver' in coreBrowserService.window) { - const observer = new coreBrowserService.window.IntersectionObserver(e => this._handleIntersectionChange(e[e.length - 1]), { threshold: 0 }); + this._observerDisposable?.dispose(); + if ('IntersectionObserver' in w) { + const observer = new w.IntersectionObserver(e => this._handleIntersectionChange(e[e.length - 1]), { threshold: 0 }); observer.observe(screenElement); - this.register({ dispose: () => observer.disconnect() }); + this._observerDisposable = toDisposable(() => observer.disconnect()); } } From 7ac95fab28ddb62b30d13b15238848fa1b72e9a2 Mon Sep 17 00:00:00 2001 From: jeanp413 Date: Tue, 12 Dec 2023 09:19:53 -0500 Subject: [PATCH 4/6] More fixes --- addons/addon-canvas/src/CanvasRenderer.ts | 15 +++++++++++++-- addons/addon-webgl/src/WebglRenderer.ts | 14 ++++++++++++-- src/browser/services/RenderService.ts | 5 +++++ 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/addons/addon-canvas/src/CanvasRenderer.ts b/addons/addon-canvas/src/CanvasRenderer.ts index 40b89546..a8e01b1a 100644 --- a/addons/addon-canvas/src/CanvasRenderer.ts +++ b/addons/addon-canvas/src/CanvasRenderer.ts @@ -12,7 +12,7 @@ import { ICharSizeService, ICharacterJoinerService, ICoreBrowserService, IThemeS import { EventEmitter, forwardEvent } from 'common/EventEmitter'; import { Disposable, toDisposable } from 'common/Lifecycle'; import { IBufferService, ICoreService, IDecorationService, IOptionsService } from 'common/services/Services'; -import { Terminal } from '@xterm/xterm'; +import { IDisposable, Terminal } from '@xterm/xterm'; import { CursorRenderLayer } from './CursorRenderLayer'; import { LinkRenderLayer } from './LinkRenderLayer'; import { SelectionRenderLayer } from './SelectionRenderLayer'; @@ -22,6 +22,7 @@ import { IRenderLayer } from './Types'; export class CanvasRenderer extends Disposable implements IRenderer { private _renderLayers: IRenderLayer[]; private _devicePixelRatio: number; + private _observerDisposable : IDisposable| undefined; public dimensions: IRenderDimensions; @@ -60,7 +61,12 @@ export class CanvasRenderer extends Disposable implements IRenderer { this._devicePixelRatio = this._coreBrowserService.dpr; this._updateDimensions(); - this.register(observeDevicePixelDimensions(this._renderLayers[0].canvas, this._coreBrowserService.window, (w, h) => this._setCanvasDevicePixelDimensions(w, h))); + this._observerDisposable = observeDevicePixelDimensions(this._renderLayers[0].canvas, this._coreBrowserService.window, (w, h) => this._setCanvasDevicePixelDimensions(w, h)); + this.register(this._coreBrowserService.onWindowChange(w => { + this._observerDisposable?.dispose(); + this._observerDisposable = observeDevicePixelDimensions(this._renderLayers[0].canvas, w, (w, h) => this._setCanvasDevicePixelDimensions(w, h)); + })); + this.register(toDisposable(() => { for (const l of this._renderLayers) { l.dispose(); @@ -183,4 +189,9 @@ export class CanvasRenderer extends Disposable implements IRenderer { private _requestRedrawViewport(): void { this._onRequestRedraw.fire({ start: 0, end: this._bufferService.rows - 1 }); } + + public override dispose(): void { + this._observerDisposable?.dispose(); + super.dispose(); + } } diff --git a/addons/addon-webgl/src/WebglRenderer.ts b/addons/addon-webgl/src/WebglRenderer.ts index 2bccc9b7..db3c28c8 100644 --- a/addons/addon-webgl/src/WebglRenderer.ts +++ b/addons/addon-webgl/src/WebglRenderer.ts @@ -19,7 +19,7 @@ import { AttributeData } from 'common/buffer/AttributeData'; import { CellData } from 'common/buffer/CellData'; import { Attributes, Content, NULL_CELL_CHAR, NULL_CELL_CODE } from 'common/buffer/Constants'; import { ICoreService, IDecorationService, IOptionsService } from 'common/services/Services'; -import { Terminal } from '@xterm/xterm'; +import { IDisposable, Terminal } from '@xterm/xterm'; import { GlyphRenderer } from './GlyphRenderer'; import { RectangleRenderer } from './RectangleRenderer'; import { COMBINED_CHAR_BIT_MASK, RENDER_MODEL_BG_OFFSET, RENDER_MODEL_EXT_OFFSET, RENDER_MODEL_FG_OFFSET, RENDER_MODEL_INDICIES_PER_CELL, RenderModel } from './RenderModel'; @@ -33,6 +33,7 @@ export class WebglRenderer extends Disposable implements IRenderer { private _charAtlasDisposable = this.register(new MutableDisposable()); private _charAtlas: ITextureAtlas | undefined; private _devicePixelRatio: number; + private _observerDisposable : IDisposable| undefined; private _model: RenderModel = new RenderModel(); private _workCell: CellData = new CellData(); @@ -123,7 +124,11 @@ export class WebglRenderer extends Disposable implements IRenderer { this._requestRedrawViewport(); })); - this.register(observeDevicePixelDimensions(this._canvas, this._coreBrowserService.window, (w, h) => this._setCanvasDevicePixelDimensions(w, h))); + this._observerDisposable = observeDevicePixelDimensions(this._canvas, this._coreBrowserService.window, (w, h) => this._setCanvasDevicePixelDimensions(w, h)); + this.register(this._coreBrowserService.onWindowChange(w => { + this._observerDisposable?.dispose(); + this._observerDisposable = observeDevicePixelDimensions(this._canvas, w, (w, h) => this._setCanvasDevicePixelDimensions(w, h)); + })); this._core.screenElement!.appendChild(this._canvas); @@ -594,6 +599,11 @@ export class WebglRenderer extends Disposable implements IRenderer { const cursorY = this._terminal.buffer.active.cursorY; this._onRequestRedraw.fire({ start: cursorY, end: cursorY }); } + + public override dispose(): void { + this._observerDisposable?.dispose(); + super.dispose(); + } } // TODO: Share impl with core diff --git a/src/browser/services/RenderService.ts b/src/browser/services/RenderService.ts index f1d25fd2..e8d59f7e 100644 --- a/src/browser/services/RenderService.ts +++ b/src/browser/services/RenderService.ts @@ -283,4 +283,9 @@ export class RenderService extends Disposable implements IRenderService { public clear(): void { this._renderer.value?.clear(); } + + public override dispose(): void { + this._observerDisposable?.dispose(); + super.dispose(); + } } From 81a4d860c5d4622c68f9ea829d0b0298a3e605be Mon Sep 17 00:00:00 2001 From: jeanp413 Date: Tue, 12 Dec 2023 09:21:55 -0500 Subject: [PATCH 5/6] :lipstick: --- addons/addon-canvas/src/CanvasRenderer.ts | 2 +- addons/addon-webgl/src/WebglRenderer.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/addon-canvas/src/CanvasRenderer.ts b/addons/addon-canvas/src/CanvasRenderer.ts index a8e01b1a..947213d4 100644 --- a/addons/addon-canvas/src/CanvasRenderer.ts +++ b/addons/addon-canvas/src/CanvasRenderer.ts @@ -22,7 +22,7 @@ import { IRenderLayer } from './Types'; export class CanvasRenderer extends Disposable implements IRenderer { private _renderLayers: IRenderLayer[]; private _devicePixelRatio: number; - private _observerDisposable : IDisposable| undefined; + private _observerDisposable: IDisposable | undefined; public dimensions: IRenderDimensions; diff --git a/addons/addon-webgl/src/WebglRenderer.ts b/addons/addon-webgl/src/WebglRenderer.ts index db3c28c8..b5f378c2 100644 --- a/addons/addon-webgl/src/WebglRenderer.ts +++ b/addons/addon-webgl/src/WebglRenderer.ts @@ -33,7 +33,7 @@ export class WebglRenderer extends Disposable implements IRenderer { private _charAtlasDisposable = this.register(new MutableDisposable()); private _charAtlas: ITextureAtlas | undefined; private _devicePixelRatio: number; - private _observerDisposable : IDisposable| undefined; + private _observerDisposable: IDisposable | undefined; private _model: RenderModel = new RenderModel(); private _workCell: CellData = new CellData(); From 9f995c6ff8cff1203c099db8b5b38ef3097d8540 Mon Sep 17 00:00:00 2001 From: Jean Pierre Date: Fri, 15 Dec 2023 16:03:37 +0000 Subject: [PATCH 6/6] :lipstick: --- addons/addon-canvas/src/CanvasRenderer.ts | 16 +++++----------- addons/addon-webgl/src/WebglRenderer.ts | 14 ++++---------- src/browser/services/RenderService.ts | 11 ++--------- 3 files changed, 11 insertions(+), 30 deletions(-) diff --git a/addons/addon-canvas/src/CanvasRenderer.ts b/addons/addon-canvas/src/CanvasRenderer.ts index 947213d4..148ae736 100644 --- a/addons/addon-canvas/src/CanvasRenderer.ts +++ b/addons/addon-canvas/src/CanvasRenderer.ts @@ -10,9 +10,9 @@ import { createRenderDimensions } from 'browser/renderer/shared/RendererUtils'; import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/renderer/shared/Types'; import { ICharSizeService, ICharacterJoinerService, ICoreBrowserService, IThemeService } from 'browser/services/Services'; import { EventEmitter, forwardEvent } from 'common/EventEmitter'; -import { Disposable, toDisposable } from 'common/Lifecycle'; +import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle'; import { IBufferService, ICoreService, IDecorationService, IOptionsService } from 'common/services/Services'; -import { IDisposable, Terminal } from '@xterm/xterm'; +import { Terminal } from '@xterm/xterm'; import { CursorRenderLayer } from './CursorRenderLayer'; import { LinkRenderLayer } from './LinkRenderLayer'; import { SelectionRenderLayer } from './SelectionRenderLayer'; @@ -22,7 +22,7 @@ import { IRenderLayer } from './Types'; export class CanvasRenderer extends Disposable implements IRenderer { private _renderLayers: IRenderLayer[]; private _devicePixelRatio: number; - private _observerDisposable: IDisposable | undefined; + private _observerDisposable = this.register(new MutableDisposable()); public dimensions: IRenderDimensions; @@ -61,10 +61,9 @@ export class CanvasRenderer extends Disposable implements IRenderer { this._devicePixelRatio = this._coreBrowserService.dpr; this._updateDimensions(); - this._observerDisposable = observeDevicePixelDimensions(this._renderLayers[0].canvas, this._coreBrowserService.window, (w, h) => this._setCanvasDevicePixelDimensions(w, h)); + this._observerDisposable.value = observeDevicePixelDimensions(this._renderLayers[0].canvas, this._coreBrowserService.window, (w, h) => this._setCanvasDevicePixelDimensions(w, h)); this.register(this._coreBrowserService.onWindowChange(w => { - this._observerDisposable?.dispose(); - this._observerDisposable = observeDevicePixelDimensions(this._renderLayers[0].canvas, w, (w, h) => this._setCanvasDevicePixelDimensions(w, h)); + this._observerDisposable.value = observeDevicePixelDimensions(this._renderLayers[0].canvas, w, (w, h) => this._setCanvasDevicePixelDimensions(w, h)); })); this.register(toDisposable(() => { @@ -189,9 +188,4 @@ export class CanvasRenderer extends Disposable implements IRenderer { private _requestRedrawViewport(): void { this._onRequestRedraw.fire({ start: 0, end: this._bufferService.rows - 1 }); } - - public override dispose(): void { - this._observerDisposable?.dispose(); - super.dispose(); - } } diff --git a/addons/addon-webgl/src/WebglRenderer.ts b/addons/addon-webgl/src/WebglRenderer.ts index b5f378c2..f2f2c83e 100644 --- a/addons/addon-webgl/src/WebglRenderer.ts +++ b/addons/addon-webgl/src/WebglRenderer.ts @@ -19,7 +19,7 @@ import { AttributeData } from 'common/buffer/AttributeData'; import { CellData } from 'common/buffer/CellData'; import { Attributes, Content, NULL_CELL_CHAR, NULL_CELL_CODE } from 'common/buffer/Constants'; import { ICoreService, IDecorationService, IOptionsService } from 'common/services/Services'; -import { IDisposable, Terminal } from '@xterm/xterm'; +import { Terminal } from '@xterm/xterm'; import { GlyphRenderer } from './GlyphRenderer'; import { RectangleRenderer } from './RectangleRenderer'; import { COMBINED_CHAR_BIT_MASK, RENDER_MODEL_BG_OFFSET, RENDER_MODEL_EXT_OFFSET, RENDER_MODEL_FG_OFFSET, RENDER_MODEL_INDICIES_PER_CELL, RenderModel } from './RenderModel'; @@ -33,7 +33,7 @@ export class WebglRenderer extends Disposable implements IRenderer { private _charAtlasDisposable = this.register(new MutableDisposable()); private _charAtlas: ITextureAtlas | undefined; private _devicePixelRatio: number; - private _observerDisposable: IDisposable | undefined; + private _observerDisposable = this.register(new MutableDisposable()); private _model: RenderModel = new RenderModel(); private _workCell: CellData = new CellData(); @@ -124,10 +124,9 @@ export class WebglRenderer extends Disposable implements IRenderer { this._requestRedrawViewport(); })); - this._observerDisposable = observeDevicePixelDimensions(this._canvas, this._coreBrowserService.window, (w, h) => this._setCanvasDevicePixelDimensions(w, h)); + this._observerDisposable.value = observeDevicePixelDimensions(this._canvas, this._coreBrowserService.window, (w, h) => this._setCanvasDevicePixelDimensions(w, h)); this.register(this._coreBrowserService.onWindowChange(w => { - this._observerDisposable?.dispose(); - this._observerDisposable = observeDevicePixelDimensions(this._canvas, w, (w, h) => this._setCanvasDevicePixelDimensions(w, h)); + this._observerDisposable.value = observeDevicePixelDimensions(this._canvas, w, (w, h) => this._setCanvasDevicePixelDimensions(w, h)); })); this._core.screenElement!.appendChild(this._canvas); @@ -599,11 +598,6 @@ export class WebglRenderer extends Disposable implements IRenderer { const cursorY = this._terminal.buffer.active.cursorY; this._onRequestRedraw.fire({ start: cursorY, end: cursorY }); } - - public override dispose(): void { - this._observerDisposable?.dispose(); - super.dispose(); - } } // TODO: Share impl with core diff --git a/src/browser/services/RenderService.ts b/src/browser/services/RenderService.ts index e8d59f7e..c2cb9a05 100644 --- a/src/browser/services/RenderService.ts +++ b/src/browser/services/RenderService.ts @@ -3,7 +3,6 @@ * @license MIT */ -import { IDisposable } from 'common/Types'; import { RenderDebouncer } from 'browser/RenderDebouncer'; import { IRenderDebouncerWithCallback } from 'browser/Types'; import { IRenderDimensions, IRenderer } from 'browser/renderer/shared/Types'; @@ -25,7 +24,7 @@ export class RenderService extends Disposable implements IRenderService { private _renderer: MutableDisposable = this.register(new MutableDisposable()); private _renderDebouncer: IRenderDebouncerWithCallback; private _pausedResizeTask = new DebouncedIdleTask(); - private _observerDisposable: IDisposable | undefined; + private _observerDisposable = this.register(new MutableDisposable()); private _isPaused: boolean = false; private _needsFullRefresh: boolean = false; @@ -110,11 +109,10 @@ export class RenderService extends Disposable implements IRenderService { private _registerIntersectionObserver(w: Window & typeof globalThis, screenElement: HTMLElement): void { // Detect whether IntersectionObserver is detected and enable renderer pause // and resume based on terminal visibility if so - this._observerDisposable?.dispose(); if ('IntersectionObserver' in w) { const observer = new w.IntersectionObserver(e => this._handleIntersectionChange(e[e.length - 1]), { threshold: 0 }); observer.observe(screenElement); - this._observerDisposable = toDisposable(() => observer.disconnect()); + this._observerDisposable.value = toDisposable(() => observer.disconnect()); } } @@ -283,9 +281,4 @@ export class RenderService extends Disposable implements IRenderService { public clear(): void { this._renderer.value?.clear(); } - - public override dispose(): void { - this._observerDisposable?.dispose(); - super.dispose(); - } }