Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle glyph widths up to the maximum device texture size #5278

Merged
merged 5 commits into from
Jan 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion addons/addon-webgl/src/WebglRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 _deviceMaxTextureSize: number;
private _observerDisposable = this._register(new MutableDisposable());

private _model: RenderModel = new RenderModel();
Expand Down Expand Up @@ -102,6 +103,8 @@ export class WebglRenderer extends Disposable implements IRenderer {
throw new Error('WebGL2 not supported ' + this._gl);
}

this._deviceMaxTextureSize = this._gl.getParameter(this._gl.MAX_TEXTURE_SIZE);

this._register(addDisposableListener(this._canvas, 'webglcontextlost', (e) => {
console.log('webglcontextlost event received');
// Prevent the default behavior in order to enable WebGL context restoration.
Expand Down Expand Up @@ -272,7 +275,8 @@ export class WebglRenderer extends Disposable implements IRenderer {
this.dimensions.device.cell.height,
this.dimensions.device.char.width,
this.dimensions.device.char.height,
this._coreBrowserService.dpr
this._coreBrowserService.dpr,
this._deviceMaxTextureSize
);
if (this._charAtlas !== atlas) {
this._onChangeTextureAtlas.fire(atlas.pages[0].canvas);
Expand Down
3 changes: 2 additions & 1 deletion addons/addon-webgl/src/renderLayer/BaseRenderLayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ export abstract class BaseRenderLayer extends Disposable implements IRenderLayer
if (this._deviceCharWidth <= 0 && this._deviceCharHeight <= 0) {
return;
}
this._charAtlas = acquireTextureAtlas(terminal, this._optionsService.rawOptions, colorSet, this._deviceCellWidth, this._deviceCellHeight, this._deviceCharWidth, this._deviceCharHeight, this._coreBrowserService.dpr);

this._charAtlas = acquireTextureAtlas(terminal, this._optionsService.rawOptions, colorSet, this._deviceCellWidth, this._deviceCellHeight, this._deviceCharWidth, this._deviceCharHeight, this._coreBrowserService.dpr, 2048);
this._charAtlas.warmUp();
}

Expand Down
5 changes: 3 additions & 2 deletions src/browser/renderer/shared/CharAtlasCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@ export function acquireTextureAtlas(
deviceCellHeight: number,
deviceCharWidth: number,
deviceCharHeight: number,
devicePixelRatio: number
devicePixelRatio: number,
deviceMaxTextureSize: number
): ITextureAtlas {
const newConfig = generateConfig(deviceCellWidth, deviceCellHeight, deviceCharWidth, deviceCharHeight, options, colors, devicePixelRatio);
const newConfig = generateConfig(deviceCellWidth, deviceCellHeight, deviceCharWidth, deviceCharHeight, options, colors, devicePixelRatio, deviceMaxTextureSize);

// Check to see if the terminal already owns this config
for (let i = 0; i < charAtlasCache.length; i++) {
Expand Down
3 changes: 2 additions & 1 deletion src/browser/renderer/shared/CharAtlasUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { ITerminalOptions } from '@xterm/xterm';
import { IColorSet, ReadonlyColorSet } from 'browser/Types';
import { NULL_COLOR } from 'common/Color';

export function generateConfig(deviceCellWidth: number, deviceCellHeight: number, deviceCharWidth: number, deviceCharHeight: number, options: Required<ITerminalOptions>, colors: ReadonlyColorSet, devicePixelRatio: number): ICharAtlasConfig {
export function generateConfig(deviceCellWidth: number, deviceCellHeight: number, deviceCharWidth: number, deviceCharHeight: number, options: Required<ITerminalOptions>, colors: ReadonlyColorSet, devicePixelRatio: number, deviceMaxTextureSize: number): ICharAtlasConfig {
// null out some fields that don't matter
const clonedColors: IColorSet = {
foreground: colors.foreground,
Expand All @@ -34,6 +34,7 @@ export function generateConfig(deviceCellWidth: number, deviceCellHeight: number
return {
customGlyphs: options.customGlyphs,
devicePixelRatio,
deviceMaxTextureSize,
letterSpacing: options.letterSpacing,
lineHeight: options.lineHeight,
deviceCellWidth: deviceCellWidth,
Expand Down
25 changes: 24 additions & 1 deletion src/browser/renderer/shared/TextureAtlas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export class TextureAtlas implements ITextureAtlas {

// The set of atlas pages that can be written to
private _activePages: AtlasPage[] = [];
private _overflowSizePage: AtlasPage | undefined;

private _tmpCanvas: HTMLCanvasElement;
// A temporary context that glyphs are drawn to before being transfered to the atlas.
Expand Down Expand Up @@ -431,7 +432,7 @@ export class TextureAtlas implements ITextureAtlas {
// Allow 1 cell width per character, with a minimum of 2 (CJK), plus some padding. This is used
// to draw the glyph to the canvas as well as to restrict the bounding box search to ensure
// giant ligatures (eg. =====>) don't impact overall performance.
const allowedWidth = Math.min(this._config.deviceCellWidth * Math.max(chars.length, 2) + TMP_CANVAS_GLYPH_PADDING * 2, this._textureSize);
const allowedWidth = Math.min(this._config.deviceCellWidth * Math.max(chars.length, 2) + TMP_CANVAS_GLYPH_PADDING * 2, this._config.deviceMaxTextureSize);
if (this._tmpCanvas.width < allowedWidth) {
this._tmpCanvas.width = allowedWidth;
}
Expand Down Expand Up @@ -772,6 +773,27 @@ export class TextureAtlas implements ITextureAtlas {
}
}

// Create a new page for oversized glyphs as they come up
if (rasterizedGlyph.size.x > this._textureSize) {
if (!this._overflowSizePage) {
this._overflowSizePage = new AtlasPage(this._document, this._config.deviceMaxTextureSize);
this.pages.push(this._overflowSizePage);

// Request the model to be cleared to refresh all texture pages.
this._requestClearModel = true;
this._onAddTextureAtlasCanvas.fire(this._overflowSizePage.canvas);
}
activePage = this._overflowSizePage;
activeRow = this._overflowSizePage.currentRow;
// Move to next row if necessary
if (activeRow.x + rasterizedGlyph.size.x >= activePage.canvas.width) {
activeRow.x = 0;
activeRow.y += activeRow.height;
activeRow.height = 0;
}
break;
}

// Create a new page if too much vertical space would be wasted or there is not enough room
// left in the page. The previous active row will become fixed in the process as it now has a
// fixed height
Expand All @@ -782,6 +804,7 @@ export class TextureAtlas implements ITextureAtlas {
if (activePage.currentRow.y + activePage.currentRow.height + rasterizedGlyph.size.y >= activePage.canvas.height) {
// Find the first page with room to create the new row on
let candidatePage: AtlasPage | undefined;

for (const p of this._activePages) {
if (p.currentRow.y + p.currentRow.height + rasterizedGlyph.size.y < p.canvas.height) {
candidatePage = p;
Expand Down
1 change: 1 addition & 0 deletions src/browser/renderer/shared/Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type { Event } from 'vs/base/common/event';
export interface ICharAtlasConfig {
customGlyphs: boolean;
devicePixelRatio: number;
deviceMaxTextureSize: number;
letterSpacing: number;
lineHeight: number;
fontSize: number;
Expand Down
Loading