Skip to content

Load edge visibility accessors as typed arrays to cut JS heap use#13643

Open
markschlosseratbentley wants to merge 5 commits into
mainfrom
edge_visibility_jsheap_fixes
Open

Load edge visibility accessors as typed arrays to cut JS heap use#13643
markschlosseratbentley wants to merge 5 commits into
mainfrom
edge_visibility_jsheap_fixes

Conversation

@markschlosseratbentley

@markschlosseratbentley markschlosseratbentley commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Description

GltfLoader.loadEdgeVisibility loaded edge-visibility accessors (visibility, silhouetteNormals, lineStrings[].indices) via loadAccessor, converting binary data into plain JS number arrays retained for the tileset's lifetime. On edge-heavy tilesets this inflated JS heap ~12x (39.5 MB -> ~460 MB on a 306-tile test dataset).

  • Load all three accessors with loadAccessorTypedArray instead
  • EdgeVisibilityPipelineStage now reads silhouetteNormals as a flat packed Int8Array (was Cartesian3[])
  • Specs updated for the new shape; added a value-level decode test for silhouette face normals

Measured heap delta after fix: ~460 MB -> 45 MB (Tafe_Sydney), 31.5 MB -> 6.6 MB (04_Plant). FPS/GPU memory unchanged.

Note: primitive.edgeVisibility.silhouetteNormals changes shape from Cartesian3[] to flat Int8Array (internal/undocumented API).

Explanation

Plain JS number arrays store elements as V8 tagged values or heap-allocated doubles rather than raw bytes, so 1-byte binary values inflate several-fold; a heap allocation profile attributed ~340 MB of the regression to loadAccessorValues. Typed arrays store the data as a compact binary buffer, 1 byte per element.

Issue number and link

#13640

Testing plan

  • All EdgeVisibility unit specs pass (gulp test --webglStub), including new typed-array assertions and a value-level silhouette-normal decode test.
  • Benchmarked with a Playwright harness (fresh browser per run, forced GC, empty-scene baseline delta): on a 306-tile edge-heavy tileset, JS heap delta dropped ~460 MB -> ~45 MB; FPS, GPU bytes, and triangle counts unchanged. Control runs on unpatched builds reproduced the original numbers.
  • Manually verified silhouette edge rendering in Sandcastle.

Results of benchmarking using Playwright harness

Before these changes

---
config:
  themeVariables:
    xyChart:
      plotColorPalette: "#1f77b4, #ff7f0e, #2ca02c, #d62728, #9467bd"
---
xychart-beta
    title "JS heap delta (peak view), as a multiple of no-ext"
    x-axis ["A0", "A1", "B0", "B1", "C0", "C1", "D0", "D1", "E0", "E1"]
    y-axis "relative" 0 --> 22.0
    bar [1.0, 5.7, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0]
    bar [-1.0, -1.0, 1.0, 6.5, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0]
    bar [-1.0, -1.0, -1.0, -1.0, 1.0, 1.3, -1.0, -1.0, -1.0, -1.0]
    bar [-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 19.2, -1.0, -1.0]
    bar [-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 11.7]
Loading
Bar Dataset Variant Value
A0 Dataset A no-ext 5.5 MB
A1 Dataset A edges 31.3 MB
B0 Dataset B no-ext 20.3 MB
B1 Dataset B edges 131.4 MB
C0 Dataset C no-ext 13.5 MB
C1 Dataset C edges 17.2 MB
D0 Dataset D no-ext 13.8 MB
D1 Dataset D edges 264.4 MB
E0 Dataset E no-ext 37.5 MB
E1 Dataset E edges 438.5 MB

After these changes

---
config:
  themeVariables:
    xyChart:
      plotColorPalette: "#1f77b4, #ff7f0e, #2ca02c, #d62728, #9467bd"
---
xychart-beta
    title "JS heap delta (peak view), as a multiple of no-ext"
    x-axis ["A0", "A1", "B0", "B1", "C0", "C1", "D0", "D1", "E0", "E1"]
    y-axis "relative" 0 --> 1.5
    bar [1.0, 1.1, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0]
    bar [-1.0, -1.0, 1.0, 1.3, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0]
    bar [-1.0, -1.0, -1.0, -1.0, 1.0, 1.2, -1.0, -1.0, -1.0, -1.0]
    bar [-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.2, -1.0, -1.0]
    bar [-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.2]
Loading
Bar Dataset Variant Value
A0 Dataset A no-ext 5.7 MB
A1 Dataset A edges 6.4 MB
B0 Dataset B no-ext 20.3 MB
B1 Dataset B edges 25.8 MB
C0 Dataset C no-ext 13.6 MB
C1 Dataset C edges 17.0 MB
D0 Dataset D no-ext 13.9 MB
D1 Dataset D edges 16.2 MB
E0 Dataset E no-ext 37.5 MB
E1 Dataset E edges 44.8 MB

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

AI acknowledgment

  • I used AI to generate content in this PR
  • If yes, I have reviewed the AI-generated content before submitting

If yes, I used the following Tools(s) and/or Service(s):

GitHub Copilot

If yes, I used the following Model(s):

Claude Fable 5

@github-actions

Copy link
Copy Markdown
Contributor

Thank you for the pull request, @markschlosseratbentley!

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

@markschlosseratbentley
markschlosseratbentley marked this pull request as ready for review July 23, 2026 12:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant