Skip to content

Drop power-of-two rounding for Text and gradient textures (WebGL 2 supports NPOT) #1554

Description

@obiot

Summary

Text and gradient surfaces round their offscreen canvas up to the next power of two. WebGL 2 supports non-power-of-two textures fully (including REPEAT wrap and mipmaps), so the rounding is pure waste since #1509 made the renderer WebGL 2 only.

Where

  • src/renderable/text/text.js:365-367nextPowerOfTwo(this.metrics.width / .height), with the comment already admitting "required for WebGL1, harmless for WebGL2/Canvas"
  • src/video/gradient.js:166-167 — same rounding for gradient textures

Impact

A 300×50 text label currently allocates a 512×64 canvas — about 2.2× the pixels. And because Text.draw() blits the whole canvas (renderer.drawImage(this.canvasTexture.canvas, x, y), text.js:533) rather than a metrics-sized sub-rect, the padded region is drawn too (transparent, so it looks correct). The saving is real for text-heavy UIs, and it is not only memory.

⚠️ The trap: POT rounding is also acting as allocation hysteresis

Do not just delete the two nextPowerOfTwo() calls. The resize is guarded by a grow-only check and CanvasRenderTarget.resize() never shrinks:

if (this.canvasTexture.width < width || this.canvasTexture.height < height) {
    this.canvasTexture.resize(width, height);
}

Power-of-two bucketing means a score counter going "9""10""11" usually stays inside the same bucket and never reallocates. Remove the rounding and every width change crosses the threshold, so dynamic text reallocates its backing canvas on essentially every update — a per-frame allocation regression on exactly the widgets that change most often.

So the work is to replace the growth strategy, not remove it:

  • round up to a modest multiple (e.g. 16 or 32 px), or
  • grow geometrically (e.g. ×1.25 with a floor), or
  • keep exact sizing but add explicit hysteresis (only resize when the delta exceeds a threshold)

Any of those keeps the reallocation behaviour while recovering most of the wasted pixels.

Scope

  • Replace POT rounding in text.js and gradient.js with a hysteresis-preserving growth strategy
  • Check whether anything else depends on the POT dimensions (bounds, UVs, the Canvas renderer path)
  • Verify no visual change on text-heavy examples; add a test pinning that repeated small text changes do not reallocate every update
  • isPowerOfTwo / nextPowerOfTwo remain in math.ts as public utilities regardless

Unblocked by #1509 (WebGL 2 only). Related: #1553.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions