Remove atlas pages from demo when they're merged

This commit is contained in:
Daniel Imms
2022-11-02 06:02:53 -07:00
parent abfe391d91
commit d82da195f3
5 changed files with 25 additions and 20 deletions
@@ -21,6 +21,8 @@ export class WebglAddon extends Disposable implements ITerminalAddon {
public readonly onChangeTextureAtlas = this._onChangeTextureAtlas.event;
private readonly _onAddTextureAtlasCanvas = this.register(new EventEmitter<HTMLCanvasElement>());
public readonly onAddTextureAtlasCanvas = this._onAddTextureAtlasCanvas.event;
private readonly _onRemoveTextureAtlasCanvas = this.register(new EventEmitter<HTMLCanvasElement>());
public readonly onRemoveTextureAtlasCanvas = this._onRemoveTextureAtlasCanvas.event;
private readonly _onContextLoss = this.register(new EventEmitter<void>());
public readonly onContextLoss = this._onContextLoss.event;
@@ -67,6 +69,7 @@ export class WebglAddon extends Disposable implements ITerminalAddon {
this.register(forwardEvent(this._renderer.onContextLoss, this._onContextLoss));
this.register(forwardEvent(this._renderer.onChangeTextureAtlas, this._onChangeTextureAtlas));
this.register(forwardEvent(this._renderer.onAddTextureAtlasCanvas, this._onAddTextureAtlasCanvas));
this.register(forwardEvent(this._renderer.onRemoveTextureAtlasCanvas, this._onRemoveTextureAtlasCanvas));
renderService.setRenderer(this._renderer);
this.register(toDisposable(() => {
@@ -53,6 +53,8 @@ export class WebglRenderer extends Disposable implements IRenderer {
public readonly onChangeTextureAtlas = this._onChangeTextureAtlas.event;
private readonly _onAddTextureAtlasCanvas = this.register(new EventEmitter<HTMLCanvasElement>());
public readonly onAddTextureAtlasCanvas = this._onAddTextureAtlasCanvas.event;
private readonly _onRemoveTextureAtlasCanvas = this.register(new EventEmitter<HTMLCanvasElement>());
public readonly onRemoveTextureAtlasCanvas = this._onRemoveTextureAtlasCanvas.event;
private readonly _onRequestRedraw = this.register(new EventEmitter<IRequestRedrawEvent>());
public readonly onRequestRedraw = this._onRequestRedraw.event;
private readonly _onContextLoss = this.register(new EventEmitter<void>());
@@ -270,7 +272,8 @@ export class WebglRenderer extends Disposable implements IRenderer {
this._onChangeTextureAtlas.fire(atlas.pages[0].canvas);
this._charAtlasDisposable = getDisposeArrayDisposable([
atlas.onRequestRedrawViewport(() => this._requestRedrawViewport()),
forwardEvent(atlas.onAddTextureAtlasCanvas, this._onAddTextureAtlasCanvas)
forwardEvent(atlas.onAddTextureAtlasCanvas, this._onAddTextureAtlasCanvas),
forwardEvent(atlas.onRemoveTextureAtlasCanvas, this._onRemoveTextureAtlasCanvas)
]);
}
this._charAtlas = atlas;
+4
View File
@@ -279,6 +279,7 @@ function createTerminal(): void {
setTextureAtlas(addons.webgl.instance.textureAtlas);
addons.webgl.instance.onChangeTextureAtlas(e => setTextureAtlas(e));
addons.webgl.instance.onAddTextureAtlasCanvas(e => appendTextureAtlas(e));
addons.webgl.instance.onRemoveTextureAtlasCanvas(e => removeTextureAtlas(e));
}
}, 0);
@@ -662,6 +663,9 @@ function appendTextureAtlas(e: HTMLCanvasElement): void {
styleAtlasPage(e);
document.querySelector('#texture-atlas').appendChild(e);
}
function removeTextureAtlas(e: HTMLCanvasElement): void {
e.remove();
}
function styleAtlasPage(e: HTMLCanvasElement): void {
e.style.width = `${e.width / window.devicePixelRatio}px`;
e.style.height = `${e.height / window.devicePixelRatio}px`;
+13 -19
View File
@@ -38,7 +38,8 @@ const enum Constants {
ROW_PIXEL_THRESHOLD = 2,
/**
* The maximum texture size regardless of what the actual hardware maximum turns out to be. This
* is enforced to ensure uploading the texture still finishes in a reasonable amount of time.
* is enforced to ensure uploading the texture still finishes in a reasonable amount of time. A
* 4096 squared image takes up 16MB of GPU memory.
*/
FORCED_MAX_TEXTURE_SIZE = 4096
}
@@ -81,6 +82,8 @@ export class TextureAtlas implements ITextureAtlas {
public readonly onRequestRedrawViewport = this._onRequestRedrawViewport.event;
private readonly _onAddTextureAtlasCanvas = new EventEmitter<HTMLCanvasElement>();
public readonly onAddTextureAtlasCanvas = this._onAddTextureAtlasCanvas.event;
private readonly _onRemoveTextureAtlasCanvas = new EventEmitter<HTMLCanvasElement>();
public readonly onRemoveTextureAtlasCanvas = this._onRemoveTextureAtlasCanvas.event;
constructor(
private readonly _document: Document,
@@ -145,17 +148,14 @@ export class TextureAtlas implements ITextureAtlas {
}
private _createNewPage(): AtlasPage {
// Try merge the set of the 4 most used pages of the largest size. This is is deferred to a
// microtask to ensure it does not interrupt textures that will be rendered in the current
// animation frame which would result in blank rendered areas. This is actually not that
// expensive relative to drawing the glyphs, so there is no need to wait for an idle callback.
if (TextureAtlas.maxAtlasPages && this._pages.length >= Math.max(4, TextureAtlas.maxAtlasPages / 2)) {
// TODO: If the viewport has enough glyphs to fill the new page, there will be a webgl error
queueMicrotask(() => {
// Migrate over 1 page at a time due to the time it takes to iterate over glyphs
// Find the set of the largest 4 images below the maximum size with the highest percentages used the 4 most used pages
// Find the set of the largest 4 images, below the maximum size, with the highest
// percentages used
const pagesBySize = this._pages.filter(e => {
return e.canvas.width * 2 <= (TextureAtlas.maxTextureSize || Constants.FORCED_MAX_TEXTURE_SIZE);
}).sort((a, b) => {
@@ -175,30 +175,25 @@ export class TextureAtlas implements ITextureAtlas {
}
}
// TODO: This is slow, need to sort after slice so _pages doesn't get sorted
const mergingPages = pagesBySize.slice(sameSizeI, sameSizeI + 4); // .sort((a, b) => a.percentageUsed < b.percentageUsed ? 1 : -1);
// Gather details of the merge
const mergingPages = pagesBySize.slice(sameSizeI, sameSizeI + 4);
const sortedMergingPagesIndexes = mergingPages.map(e => e.glyphs[0].texturePage).sort((a, b) => a > b ? 1 : -1);
// TODO: Pull texture page index in a nicer way
const mergedPageIndex = sortedMergingPagesIndexes[0];
// Merge into the new page
const mergedPage = this._mergePages(mergingPages, mergedPageIndex);
// (console as any).image(mergedPage.canvas);
mergedPage.hasCanvasChanged = true;
// this._pages[0] = mergedPage;
// Replace an old merging page with the merged
// Replace the first _merging_ page with the _merged_ page
this._pages[mergedPageIndex] = mergedPage;
// TODO: Splice other 3 pages, shifting all other texture page props
// Delete the other 3 pages, shifting glyph texture pages as needed
for (let i = sortedMergingPagesIndexes.length - 1; i >= 1; i--) {
this._deletePage(sortedMergingPagesIndexes[i]);
}
// Request the model to be cleared to refresh all texture pages.
this._requestClearModel = true;
this._onAddTextureAtlasCanvas.fire(mergedPage.canvas);
});
}
@@ -228,8 +223,7 @@ export class TextureAtlas implements ITextureAtlas {
g.texturePositionClipSpace.y = g.texturePosition.y / mergedSize;
}
p.ctx.fillStyle = 'green';
p.ctx.fillRect(0, 0, p.canvas.width, p.canvas.height);
this._onRemoveTextureAtlasCanvas.fire(p.canvas);
// Remove the merging page from active pages if it was there
const index = this._activePages.indexOf(p);
+1
View File
@@ -91,6 +91,7 @@ export interface ITextureAtlas extends IDisposable {
onRequestRedrawViewport: IEvent<void>;
onAddTextureAtlasCanvas: IEvent<HTMLCanvasElement>;
onRemoveTextureAtlasCanvas: IEvent<HTMLCanvasElement>;
/**
* Warm up the texture atlas, adding common glyphs to avoid slowing early frame.