Skip to content

TextureCache.get(image, frame): framewidth/frameheight refinement is a no-op for every atlas format #1489

@obiot

Description

@obiot

Summary

TextureCache.get(image, { framewidth, frameheight }) in packages/melonjs/src/video/texture/cache.js:166 documents itself as picking the atlas under image whose dimensions match the requested frame size, useful when an image has multiple atlases registered (e.g. a cropped variant alongside the full-image default). In practice the refinement loop is a no-op against every atlas format the engine produces — it has been dormant for as long as the parser has been unified.

Surfaced while writing adversarial tests for #1448; not a live bug today, but worth pinning before it bites.

How it's silent

this.cache.forEach((value, key) => {
    const _atlas = value.getAtlas();
    if (
        key === image &&
        _atlas.width === atlas.framewidth &&   // ← always undefined === number
        _atlas.height === atlas.frameheight
    ) {
        entry = value;
    }
});
  • getAtlas() returns this.atlases.get(this.activeAtlas).
  • this.atlases is populated by parseTexturePacker(atlas, this) for every format — TexturePacker, Aseprite, ShoeBox, AND the "melonJS" format produced by createAtlas() (the pattern path) all route through the same parser.
  • parseTexturePacker (packages/melonjs/src/video/texture/parser/texturepacker.js:41) builds a frame-keyed dict: atlas[frame.filename] = { width: s.w, height: s.h, ... }. So _atlas is { default: { width: 64, height: 64, ... } }, not { width: 64, height: 64 }.
  • _atlas.width is always undefined. undefined === framewidth is always false. The loop never updates entry. The function always returns cache.get(image)[0].

Why nothing observably breaks today

  • Single-atlas-per-image is the common case (sprites, sprite sheets, tilesets). Refining "the only one" → "still the only one" is no-op anyway.
  • The existing test texture.spec.js > should retrieve specific atlas by frame dimensions only asserts toBeDefined() — which holds even when the refinement no-ops, so the test passes against the broken behavior.
  • The WebGL: createPattern cache-key collision invalidates earlier pattern handles for the same image #1448 fix makes multi-atlas-per-image legitimate (one atlas per repeat mode), but those atlases share dimensions (the source image's), so the framewidth refinement still wouldn't have discriminated even with a working comparison.

Options

A. Fix the comparison to use the right path. Compare _atlas[Object.keys(_atlas)[0]].width / height or, more semantically, peek at the underlying meta.size.w / .h if accessible. Real behavioral change — could surface latent bugs in any caller that's accidentally been relying on always getting cache.get(image)[0].

B. Expose top-level .width / .height on the parsed atlas dict. Tweak parseTexturePacker (and the createAtlas "melonJS"-format equivalent) to also set top-level .width and .height on the dict so the existing comparison works as intended.

C. Drop the framewidth/frameheight argument entirely. If no caller in the engine actually relies on it (grep suggests none do), removing the dead branch keeps the API smaller and prevents future confusion. The existing test would need updating.

Recommendation

Defer to the #1410 (TextureCache + Batcher renderer-agnostic refactor) reviewer / author — they'll touch this whole API surface anyway, and the right answer (which of A/B/C) depends on whether the framewidth refinement is intentional API or a vestige.

Cross-refs

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions