Skip to content

Commit a0d0c92

Browse files
authored
Merge pull request #4732 from Tyriar/texture_utilization
Improve texture atlas utilization and fix glyph corruption when merging
2 parents 55c5a85 + aba1668 commit a0d0c92

File tree

1 file changed

+60
-47
lines changed

1 file changed

+60
-47
lines changed

src/browser/renderer/shared/TextureAtlas.ts

Lines changed: 60 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -151,50 +151,48 @@ export class TextureAtlas implements ITextureAtlas {
151151
// microtask to ensure it does not interrupt textures that will be rendered in the current
152152
// animation frame which would result in blank rendered areas. This is actually not that
153153
// expensive relative to drawing the glyphs, so there is no need to wait for an idle callback.
154-
if (TextureAtlas.maxAtlasPages && this._pages.length >= Math.max(4, TextureAtlas.maxAtlasPages / 2)) {
155-
queueMicrotask(() => {
156-
// Find the set of the largest 4 images, below the maximum size, with the highest
157-
// percentages used
158-
const pagesBySize = this._pages.filter(e => {
159-
return e.canvas.width * 2 <= (TextureAtlas.maxTextureSize || Constants.FORCED_MAX_TEXTURE_SIZE);
160-
}).sort((a, b) => {
161-
if (b.canvas.width !== a.canvas.width) {
162-
return b.canvas.width - a.canvas.width;
163-
}
164-
return b.percentageUsed - a.percentageUsed;
165-
});
166-
let sameSizeI = -1;
167-
let size = 0;
168-
for (let i = 0; i < pagesBySize.length; i++) {
169-
if (pagesBySize[i].canvas.width !== size) {
170-
sameSizeI = i;
171-
size = pagesBySize[i].canvas.width;
172-
} else if (i - sameSizeI === 3) {
173-
break;
174-
}
154+
if (TextureAtlas.maxAtlasPages && this._pages.length >= Math.max(4, TextureAtlas.maxAtlasPages)) {
155+
// Find the set of the largest 4 images, below the maximum size, with the highest
156+
// percentages used
157+
const pagesBySize = this._pages.filter(e => {
158+
return e.canvas.width * 2 <= (TextureAtlas.maxTextureSize || Constants.FORCED_MAX_TEXTURE_SIZE);
159+
}).sort((a, b) => {
160+
if (b.canvas.width !== a.canvas.width) {
161+
return b.canvas.width - a.canvas.width;
162+
}
163+
return b.percentageUsed - a.percentageUsed;
164+
});
165+
let sameSizeI = -1;
166+
let size = 0;
167+
for (let i = 0; i < pagesBySize.length; i++) {
168+
if (pagesBySize[i].canvas.width !== size) {
169+
sameSizeI = i;
170+
size = pagesBySize[i].canvas.width;
171+
} else if (i - sameSizeI === 3) {
172+
break;
175173
}
174+
}
176175

177-
// Gather details of the merge
178-
const mergingPages = pagesBySize.slice(sameSizeI, sameSizeI + 4);
179-
const sortedMergingPagesIndexes = mergingPages.map(e => e.glyphs[0].texturePage).sort((a, b) => a > b ? 1 : -1);
180-
const mergedPageIndex = sortedMergingPagesIndexes[0];
176+
// Gather details of the merge
177+
const mergingPages = pagesBySize.slice(sameSizeI, sameSizeI + 4);
178+
const sortedMergingPagesIndexes = mergingPages.map(e => e.glyphs[0].texturePage).sort((a, b) => a > b ? 1 : -1);
179+
const mergedPageIndex = this.pages.length - mergingPages.length;
181180

182-
// Merge into the new page
183-
const mergedPage = this._mergePages(mergingPages, mergedPageIndex);
184-
mergedPage.version++;
181+
// Merge into the new page
182+
const mergedPage = this._mergePages(mergingPages, mergedPageIndex);
183+
mergedPage.version++;
185184

186-
// Replace the first _merging_ page with the _merged_ page
187-
this._pages[mergedPageIndex] = mergedPage;
185+
// Delete the pages, shifting glyph texture pages as needed
186+
for (let i = sortedMergingPagesIndexes.length - 1; i >= 0; i--) {
187+
this._deletePage(sortedMergingPagesIndexes[i]);
188+
}
188189

189-
// Delete the other 3 pages, shifting glyph texture pages as needed
190-
for (let i = sortedMergingPagesIndexes.length - 1; i >= 1; i--) {
191-
this._deletePage(sortedMergingPagesIndexes[i]);
192-
}
190+
// Add the new merged page to the end
191+
this.pages.push(mergedPage);
193192

194-
// Request the model to be cleared to refresh all texture pages.
195-
this._requestClearModel = true;
196-
this._onAddTextureAtlasCanvas.fire(mergedPage.canvas);
197-
});
193+
// Request the model to be cleared to refresh all texture pages.
194+
this._requestClearModel = true;
195+
this._onAddTextureAtlasCanvas.fire(mergedPage.canvas);
198196
}
199197

200198
// All new atlas pages are created small as they are highly dynamic
@@ -756,13 +754,13 @@ export class TextureAtlas implements ITextureAtlas {
756754
}
757755
}
758756

759-
// Create a new one if too much vertical space would be wasted or there is not enough room
757+
// Create a new page if too much vertical space would be wasted or there is not enough room
760758
// left in the page. The previous active row will become fixed in the process as it now has a
761759
// fixed height
762760
if (activeRow.y + rasterizedGlyph.size.y >= activePage.canvas.height || activeRow.height > rasterizedGlyph.size.y + Constants.ROW_PIXEL_THRESHOLD) {
763761
// Create the new fixed height row, creating a new page if there isn't enough room on the
764762
// current page
765-
let wasNewPageCreated = false;
763+
let wasPageAndRowFound = false;
766764
if (activePage.currentRow.y + activePage.currentRow.height + rasterizedGlyph.size.y >= activePage.canvas.height) {
767765
// Find the first page with room to create the new row on
768766
let candidatePage: AtlasPage | undefined;
@@ -775,15 +773,30 @@ export class TextureAtlas implements ITextureAtlas {
775773
if (candidatePage) {
776774
activePage = candidatePage;
777775
} else {
778-
// Create a new page if there is no room
779-
const newPage = this._createNewPage();
780-
activePage = newPage;
781-
activeRow = newPage.currentRow;
782-
activeRow.height = rasterizedGlyph.size.y;
783-
wasNewPageCreated = true;
776+
// Before creating a new atlas page that would trigger a page merge, check if the
777+
// current active row is sufficient when ignoring the ROW_PIXEL_THRESHOLD. This will
778+
// improve texture utilization by using the available space before the page is merged
779+
// and becomes static.
780+
if (
781+
TextureAtlas.maxAtlasPages &&
782+
this._pages.length >= TextureAtlas.maxAtlasPages &&
783+
activeRow.y + rasterizedGlyph.size.y <= activePage.canvas.height &&
784+
activeRow.height >= rasterizedGlyph.size.y &&
785+
activeRow.x + rasterizedGlyph.size.x <= activePage.canvas.width
786+
) {
787+
// activePage and activeRow is already valid
788+
wasPageAndRowFound = true;
789+
} else {
790+
// Create a new page if there is no room
791+
const newPage = this._createNewPage();
792+
activePage = newPage;
793+
activeRow = newPage.currentRow;
794+
activeRow.height = rasterizedGlyph.size.y;
795+
wasPageAndRowFound = true;
796+
}
784797
}
785798
}
786-
if (!wasNewPageCreated) {
799+
if (!wasPageAndRowFound) {
787800
// Fix the current row as the new row is being added below
788801
if (activePage.currentRow.height > 0) {
789802
activePage.fixedRows.push(activePage.currentRow);

0 commit comments

Comments
 (0)