Skip to content

Conversation

@donmccurdy
Copy link
Member

@donmccurdy donmccurdy commented Nov 24, 2025

Description

Currently billboards compute texture coordinates (in 0–1 UV space) for each image in the texture atlas:

/**
* Get the texture coordinates for reading the associated image in shaders.
* @param {number} index The index of the image region.
* @param {BoundingRectangle} [result] The object into which to store the result.
* @return {BoundingRectangle} The modified result parameter or a new BoundingRectangle instance if one was not provided.
* @private
* @example
* const index = await atlas.addImage("myImage", image);
* const rectangle = atlas.computeTextureCoordinates(index);
* BoundingRectangle.pack(rectangle, bufferView);
*/
TextureAtlas.prototype.computeTextureCoordinates = function (index, result) {

Texture coordinates are then quantized to 12 bits:

/**
* Pack texture coordinates into a single float. The texture coordinates will only preserve 12 bits of precision.
*
* @param {Cartesian2} textureCoordinates The texture coordinates to compress. Both coordinates must be in the range 0.0-1.0.
* @returns {number} The packed texture coordinates.
*
*/
AttributeCompression.compressTextureCoordinates = function (
textureCoordinates,
) {

On testing all valid pixel offsets for all power-of-two atlas sizes...

const src = {x: 0, y: 0}, dst = {x: 0, y: 0};
for (let exp = 1; exp <= 12; exp++) {
  const width = 2 ** exp;
  for (let x = 0; x <= width; x++) {
    src.x = x / width;

    const encoded = AttributeCompression.compressTextureCoordinates(src);
    AttributeCompression.decompressTextureCoordinates(encoded, dst);

    const dstX = Math.round(dst.x * width);
    assert(dstX === x, `❌ Expected dstX=${dstX} to be ${x}`);
  }
}

... we find coordinates off by 1px about 25% of the time, which I believe explains the remaining issues from #5154. While we could modify the texture compression code to fix this, (1) texture coordinates outside of billboards may not correspond to integer pixel offsets at all, and (2) it's worth stepping back to consider what we're trying to do:

Ultimately each Billboard must sample from its image in the texture atlas. Images have integer pixel offsets and integer dimensions, which cannot exceed maximum texture sizes of ~16K. We need to pack pairs of these integers into 32-bit vertex attribute components. We can achieve this more simply by packing two integers into one 32-bit float directly...

const encoded = x * (2 ** 16) + y;

... skipping both integer-to-float conversions and lossy quantization.

Preview:

TODO

Issue number and link

Testing plan

Because coordinates and attribute packing differs in WebGL2 vs. WebGL1, and with or without instancing, we need to test multiple environments:

  1. WebGL2, instancing
  2. WebGL1, instancing
  3. WebGL1, no instancing

WebGL2 guarantees instancing support, so we don't need to worry about that case. If #13042 is merged before this PR (preferred) then the WebGL1 vs. WebGL2 distinction is removed and we only need to worry about instanced vs. non-instanced code paths. Optionally instancing could be required (see #13053) but I've handled the non-instanced fallback here anyway.

More detailed test plan TBD.

Author checklist

  • I have submitted a Contributor License Agreement
  • I have added my name to CONTRIBUTORS.md
  • I have updated CHANGES.md with a short summary of my change
  • I have added or updated unit tests to ensure consistent code coverage
  • I have updated the inline documentation, and included code examples where relevant
  • I have performed a self-review of my code

PR Dependency Tree

This tree was auto-generated by Charcoal

@github-actions
Copy link

Thank you for the pull request, @donmccurdy!

✅ We can confirm we have a CLA on file for you.

@donmccurdy donmccurdy force-pushed the fix/billboard-image-offset-precision branch from 6dd9ca1 to 2f3a731 Compare November 25, 2025 17:02
@donmccurdy donmccurdy changed the base branch from refactor/billboard-instancing-only to fix/textureatlas-slow-alloc November 25, 2025 17:02
@donmccurdy donmccurdy self-assigned this Nov 25, 2025
@donmccurdy donmccurdy force-pushed the fix/textureatlas-slow-alloc branch from 28213a7 to bf8835f Compare December 1, 2025 15:39
@donmccurdy donmccurdy force-pushed the fix/billboard-image-offset-precision branch from eecd3b4 to c09783a Compare December 1, 2025 15:39
@donmccurdy donmccurdy force-pushed the fix/textureatlas-slow-alloc branch 2 times, most recently from 70ee1d5 to 3c40024 Compare December 4, 2025 14:59
Base automatically changed from fix/textureatlas-slow-alloc to main December 4, 2025 16:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants