Handle overflowing to new row

This commit is contained in:
Daniel Imms
2025-01-07 06:28:59 -08:00
parent ff9a3a1067
commit a7b98de833
6 changed files with 31 additions and 18 deletions
+5 -1
View File
@@ -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();
@@ -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.
@@ -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);
@@ -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();
}
@@ -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++) {
@@ -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,
@@ -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,
+18 -13
View File
@@ -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.
@@ -75,8 +76,6 @@ export class TextureAtlas implements ITextureAtlas {
private _workAttributeData: AttributeData = new AttributeData();
private _textureSize: number = 512;
// TODO: Use actual value
private _deviceMaxTextureSize: number = 2048;
public static maxAtlasPages: number | undefined;
public static maxTextureSize: number | undefined;
@@ -433,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._deviceMaxTextureSize);
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;
}
@@ -774,19 +773,24 @@ export class TextureAtlas implements ITextureAtlas {
}
}
// TODO: Move below after page merging to ensure page limit isn't hit
// Create a new page for oversized glyphs as they come up
if (rasterizedGlyph.size.x > this._textureSize) {
// TODO: Reuse a single oversized glyphs page
const newPage = new AtlasPage(this._document, this._deviceMaxTextureSize);
this.pages.push(newPage);
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(newPage.canvas);
newPage.addGlyph(rasterizedGlyph);
activePage.fixedRows.push(newPage.currentRow);
// 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;
}
@@ -800,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;
+1
View File
@@ -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;