Skip to content

Commit af40675

Browse files
committed
fix(textureatlas): Allocate texture space less aggressively on resize
1 parent bcc5ea3 commit af40675

File tree

2 files changed

+60
-20
lines changed

2 files changed

+60
-20
lines changed

packages/engine/Source/Renderer/TextureAtlas.js

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -364,26 +364,17 @@ TextureAtlas.prototype._resize = function (context, queueOffset = 0) {
364364
toPack.push(queue[i]);
365365
}
366366

367-
// At minimum, the texture will need to scale to accommodate the largest width and height
368-
width = Math.max(maxWidth, width);
369-
height = Math.max(maxHeight, height);
367+
// At minimum, atlas must fit its largest input images. Texture coordinates are
368+
// compressed to 0–1 with 12-bit precision, so use power-of-two size to align pixels.
369+
width = CesiumMath.nextPowerOfTwo(Math.max(maxWidth, width));
370+
height = CesiumMath.nextPowerOfTwo(Math.max(maxHeight, height));
370371

371-
if (!context.webgl2) {
372-
width = CesiumMath.nextPowerOfTwo(width);
373-
height = CesiumMath.nextPowerOfTwo(height);
374-
}
375-
376-
// Determine by what factor the texture need to be scaled by at minimum
377-
const areaDifference = areaQueued;
378-
let scalingFactor = 1.0;
379-
while (areaDifference / width / height >= 1.0) {
380-
scalingFactor *= 2.0;
381-
382-
// Resize by one dimension
372+
// Iteratively double the smallest dimension until atlas area is (approximately) sufficient.
373+
while (areaQueued >= width * height) {
383374
if (width > height) {
384-
height *= scalingFactor;
375+
height *= 2;
385376
} else {
386-
width *= scalingFactor;
377+
width *= 2;
387378
}
388379
}
389380

packages/engine/Specs/Renderer/TextureAtlasSpec.js

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -694,9 +694,8 @@ describe("Scene/TextureAtlas", function () {
694694
expect(bigBlueIndex).toEqual(2);
695695
expect(redIndex).toEqual(3);
696696

697-
// Webgl1 textures should only be powers of 2
698-
const textureWidth = scene.frameState.context.webgl2 ? 20 : 32;
699-
const textureHeight = scene.frameState.context.webgl2 ? 32 : 16;
697+
const textureWidth = 32;
698+
const textureHeight = 16;
700699

701700
const texture = atlas.texture;
702701
expect(texture.pixelFormat).toEqual(PixelFormat.RGBA);
@@ -1297,6 +1296,56 @@ describe("Scene/TextureAtlas", function () {
12971296
expect(guid1).not.toEqual(guid2);
12981297
});
12991298

1299+
it("allocates appropriate space on resize", async function () {
1300+
atlas = new TextureAtlas();
1301+
1302+
// Adapted from https://github.com/CesiumGS/cesium/issues/5154, this represents a total
1303+
// of 2M pixels to be allocated.
1304+
const imageCount = 256;
1305+
const imageWidth = 128;
1306+
const imageHeight = 64;
1307+
1308+
const totalPixels = imageCount * imageWidth * imageHeight;
1309+
1310+
const imageUrl = createImageDataURL(imageWidth, imageHeight);
1311+
1312+
const promises = [];
1313+
for (let i = 0; i < imageCount; i++) {
1314+
promises.push(atlas.addImage(i.toString(), imageUrl));
1315+
}
1316+
1317+
const allPromises = Promise.all(promises);
1318+
1319+
await pollWhilePromise(allPromises, () => {
1320+
atlas.update(scene.frameState.context);
1321+
});
1322+
1323+
await allPromises;
1324+
1325+
const atlasWidth = atlas.texture.width;
1326+
const atlasHeight = atlas.texture.height;
1327+
1328+
// Allocate enough space, but not >2x more.
1329+
expect(atlasWidth * atlasHeight).toBeGreaterThan(totalPixels);
1330+
expect(atlasWidth * atlasHeight).toBeLessThanOrEqual(totalPixels * 2);
1331+
1332+
// Aspect ratio should be no more extreme than 1:2.
1333+
expect(atlasWidth / atlasHeight).toBeGreaterThanOrEqual(0.5);
1334+
expect(atlasWidth / atlasHeight).toBeLessThanOrEqual(2.0);
1335+
1336+
function createImageDataURL(width, height) {
1337+
const canvas = document.createElement("canvas");
1338+
canvas.width = width;
1339+
canvas.height = height;
1340+
1341+
const ctx = canvas.getContext("2d");
1342+
ctx.fillStyle = "green";
1343+
ctx.fillRect(0, 0, width, height);
1344+
1345+
return canvas.toDataURL();
1346+
}
1347+
});
1348+
13001349
it("destroys successfully while image is queued", async function () {
13011350
atlas = new TextureAtlas();
13021351

0 commit comments

Comments
 (0)