diff --git a/addons/addon-webgl/src/GlyphRenderer.ts b/addons/addon-webgl/src/GlyphRenderer.ts index 720427723..c8ab2f344 100644 --- a/addons/addon-webgl/src/GlyphRenderer.ts +++ b/addons/addon-webgl/src/GlyphRenderer.ts @@ -101,6 +101,7 @@ export class GlyphRenderer extends Disposable { private _atlas: ITextureAtlas | undefined; private _lastSeenPageLayoutVersion: number = -1; + private _pageOverflowWarned: boolean = false; private _activeBuffer: number = 0; private readonly _vertices: IVertices = { count: 0, @@ -375,7 +376,12 @@ export class GlyphRenderer extends Disposable { // Bind the atlas page texture if they have changed. AtlasPage.version is globally // monotonic, so a page object swap at the same index (which happens after a page merge) // is detected by the same comparison. - for (let i = 0; i < this._atlas.pages.length; i++) { + const pageCount = Math.min(this._atlas.pages.length, this._atlasTextures.length); + if (this._atlas.pages.length > this._atlasTextures.length && !this._pageOverflowWarned) { + this._pageOverflowWarned = true; + this._logService.warn(`Atlas page count (${this._atlas.pages.length}) exceeds the renderer's texture capacity (${this._atlasTextures.length}), some glyphs will not render correctly`); + } + for (let i = 0; i < pageCount; i++) { if (this._atlas.pages[i].version !== this._atlasTextures[i].version) { this._bindAtlasPageTexture(gl, this._atlas, i); } diff --git a/addons/addon-webgl/src/TextureAtlas.ts b/addons/addon-webgl/src/TextureAtlas.ts index 5368c9b46..fdcb612de 100644 --- a/addons/addon-webgl/src/TextureAtlas.ts +++ b/addons/addon-webgl/src/TextureAtlas.ts @@ -152,7 +152,7 @@ export class TextureAtlas implements ITextureAtlas { // 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)) { + if (TextureAtlas.maxAtlasPages && this._pages.length >= TextureAtlas.maxAtlasPages) { // Find the set of the largest 4 images, below the maximum size, with the highest // percentages used const pagesBySize = this._pages.filter(e => { @@ -180,31 +180,27 @@ export class TextureAtlas implements ITextureAtlas { // Only proceed with merge if we have exactly 4 same-sized pages. If not, we cannot // effectively reduce page count and merging would cause issues. if (mergingPages.length < 4 || mergingPages.some(p => p.canvas.width !== mergingPages[0].canvas.width)) { - const newPage = new AtlasPage(this._document, this._textureSize); - this._pages.push(newPage); - this._activePages.push(newPage); - this._onAddTextureAtlasCanvas.fire(newPage.canvas); - return newPage; + this._evictAllPages(); + } else { + const sortedMergingPagesIndexes = mergingPages.map(e => e.glyphs[0].texturePage).sort((a, b) => a - b); + const mergedPageIndex = this.pages.length - mergingPages.length; + + // Merge into the new page + const mergedPage = this._mergePages(mergingPages, mergedPageIndex); + mergedPage.version = ++AtlasPage.nextVersion; + + // Delete the pages, shifting glyph texture pages as needed + for (let i = sortedMergingPagesIndexes.length - 1; i >= 0; i--) { + this._deletePage(sortedMergingPagesIndexes[i]); + } + + // Add the new merged page to the end + this.pages.push(mergedPage); + + // Request the model to be cleared to refresh all texture pages. + this._pageLayoutVersion++; + this._onAddTextureAtlasCanvas.fire(mergedPage.canvas); } - - const sortedMergingPagesIndexes = mergingPages.map(e => e.glyphs[0].texturePage).sort((a, b) => a - b); - const mergedPageIndex = this.pages.length - mergingPages.length; - - // Merge into the new page - const mergedPage = this._mergePages(mergingPages, mergedPageIndex); - mergedPage.version = ++AtlasPage.nextVersion; - - // Delete the pages, shifting glyph texture pages as needed - for (let i = sortedMergingPagesIndexes.length - 1; i >= 0; i--) { - this._deletePage(sortedMergingPagesIndexes[i]); - } - - // Add the new merged page to the end - this.pages.push(mergedPage); - - // Request the model to be cleared to refresh all texture pages. - this._pageLayoutVersion++; - this._onAddTextureAtlasCanvas.fire(mergedPage.canvas); } // All new atlas pages are created small as they are highly dynamic @@ -254,6 +250,20 @@ export class TextureAtlas implements ITextureAtlas { } } + private _evictAllPages(): void { + for (const page of this._pages) { + this._onRemoveTextureAtlasCanvas.fire(page.canvas); + page.canvas.remove(); + } + this._pages.length = 0; + this._activePages.length = 0; + this._overflowSizePage = undefined; + this._cacheMap.clear(); + this._cacheMapCombined.clear(); + this._didWarmUp = false; + this._pageLayoutVersion++; + } + public getRasterizedGlyphCombinedChar(chars: string, bg: number, fg: number, ext: number, restrictToCellHeight: boolean, domContainer: HTMLElement | undefined): IRasterizedGlyph { return this._getFromCacheMap(this._cacheMapCombined, chars, bg, fg, ext, restrictToCellHeight, domContainer); } @@ -811,6 +821,9 @@ 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) { + if (TextureAtlas.maxAtlasPages && this._pages.length >= TextureAtlas.maxAtlasPages) { + this._evictAllPages(); + } this._overflowSizePage = new AtlasPage(this._document, this._config.deviceMaxTextureSize); this.pages.push(this._overflowSizePage);