spine-html 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/NOTICE.md CHANGED
@@ -14,6 +14,7 @@ redistribution includes the Spine Runtimes license and copyright notice.
14
14
 
15
15
  ## Example assets
16
16
 
17
- The spineboy skeleton, atlas, and images used by the demo are owned by Esoteric
18
- Software. They are **not redistributed** in this repository; `scripts/fetch-assets.sh`
19
- downloads them from the official spine-runtimes repository for local evaluation.
17
+ The spineboy skeleton exports (JSON and binary), atlas, and images used by the demo
18
+ and the tests are owned by Esoteric Software. They are **not redistributed** in this
19
+ repository; `scripts/fetch-assets.sh` downloads them from the official spine-runtimes
20
+ repository for local evaluation.
package/README.md CHANGED
@@ -148,12 +148,78 @@ assets.dispose();
148
148
  `spine-html` is a third-party renderer and is not affiliated with or endorsed by
149
149
  Esoteric Software.
150
150
 
151
+ ### One atlas, several skeletons
152
+
153
+ Skeletons that share an atlas — a character's `-ess` and `-pro` exports, a whole
154
+ cast packed onto one page — load in two steps instead. Calling
155
+ `loadSkeletonAssets` once per skeleton would fetch and unpack the same pages
156
+ again and mint a second set of region blob URLs:
157
+
158
+ ```ts
159
+ import { loadAtlasAssets, loadSkeletonJson, SpineHtmlRenderer } from 'spine-html';
160
+
161
+ const shared = await loadAtlasAssets({ atlasUrl: '/spineboy/spineboy.atlas' });
162
+ const [essData, proData] = await Promise.all([
163
+ loadSkeletonJson(shared, '/spineboy/spineboy-ess.json'),
164
+ loadSkeletonJson(shared, '/spineboy/spineboy-pro.json'),
165
+ ]);
166
+
167
+ const ess = new SpineHtmlRenderer(essRoot, shared.regionImages);
168
+ const pro = new SpineHtmlRenderer(proRoot, shared.regionImages);
169
+
170
+ // Unloading: every renderer first, then the one dispose() that owns the bitmaps.
171
+ ess.dispose();
172
+ pro.dispose();
173
+ shared.dispose();
174
+ ```
175
+
176
+ `loadAtlasAssets` takes the atlas half of the options above (`resolvePage`,
177
+ `crossOrigin`, `fetch`) and `loadSkeletonJson` the skeleton half (`scale`,
178
+ `fetch`). Ownership is the thing to keep straight: the atlas assets belong to
179
+ the caller, reading a skeleton against them never frees them, and one
180
+ `regionImages` map is meant to be handed to several renderers — so there is
181
+ exactly one `dispose()` for however many skeletons were read. `loadSkeletonJson`
182
+ asks only for `{ atlas }`, so a caller that built its atlas by hand (below) can
183
+ still use it for the read. `loadSkeletonAssets` is these two calls with the
184
+ atlas half kept private: use it for one atlas and one skeleton, this for the
185
+ rest.
186
+
187
+ ### Binary exports (`.skel`)
188
+
189
+ Binary exports are read by a second parser, `SkeletonBinary`, which is as big as
190
+ the JSON one. It lives behind its own entry point so that consumers who only
191
+ ever read `.json` never carry it:
192
+
193
+ ```ts
194
+ import { loadAtlasAssets, SpineHtmlRenderer } from 'spine-html';
195
+ import { loadSkeletonBinary } from 'spine-html/binary';
196
+
197
+ const shared = await loadAtlasAssets({ atlasUrl: '/spineboy/spineboy.atlas' });
198
+ const data = await loadSkeletonBinary(shared, '/spineboy/spineboy-pro.skel');
199
+
200
+ const renderer = new SpineHtmlRenderer(rootElement, shared.regionImages);
201
+ // Unloading, as above: renderer.dispose(); shared.dispose();
202
+ ```
203
+
204
+ `loadSkeletonBinary` is `loadSkeletonJson` with the parser swapped: same
205
+ arguments, the same `{ scale, fetch }` options, the same ownership rule — it
206
+ never frees `assets`, whichever way it ends. Those two calls are the binary path
207
+ in full. There is no one-call `loadSkeletonAssetsBinary`, because the atlas half
208
+ is precisely what a caller reading binary usually wants kept in hand, and hiding
209
+ it again would only earn back a line.
210
+
211
+ The subpath is the whole mechanism, so import it as written: pulling
212
+ `loadSkeletonBinary` out of `'spine-html'` is not possible, by design. Worth
213
+ knowing about the format itself: binary exports are version-locked to the
214
+ runtime that reads them, so a `.skel` written by an editor newer than your
215
+ installed `@esotericsoftware/spine-core` fails in the read, not in the fetch.
216
+
151
217
  ### Loading it yourself
152
218
 
153
219
  `loadSkeletonAssets` is optional sugar over five `spine-core` calls, and the
154
220
  package works without it. Drop to the low-level path whenever you need
155
- something it does not do one atlas shared by several skeletons, a binary
156
- export, images that are already in memory:
221
+ something no loader does — images that are already in memory, an atlas that is
222
+ not fetched from a URL at all:
157
223
 
158
224
  ```ts
159
225
  import { TextureAtlas, AtlasAttachmentLoader, SkeletonJson }
@@ -181,6 +247,35 @@ it — and calling it twice is a no-op. Load once for the page's lifetime and yo
181
247
  can ignore it; load and unload repeatedly without it and you leak an atlas per
182
248
  cycle.
183
249
 
250
+ ### What unloading frees
251
+
252
+ `revokeRegions()` — and `assets.dispose()`, which just calls it — frees the blob
253
+ URLs `unpackRegions` minted, and nothing else. The rest is pinned by objects you
254
+ own: an atlas **page image** is held by the `DomTexture` you attached to its
255
+ page, and `SkeletonData` reaches that same image through every attachment
256
+ (`region.texture.getImage()`), so a page is released only once the atlas, the
257
+ skeleton data and every renderer drawing from them are gone. `atlas.dispose()`
258
+ does not do it — `DomTexture.dispose()` is a no-op, because the image is yours.
259
+ With the `webgl` mesh backend the shared blitter also uploads each page image on
260
+ first use, but that GL texture is not the module's forever: every renderer
261
+ drawing the page holds a reference to it, `renderer.dispose()` hands those back,
262
+ and the texture is deleted once the last renderer using that page is disposed —
263
+ a later frame that needs the page uploads it again. The cache is keyed weakly by
264
+ the page image, so a renderer dropped without `dispose()` costs GPU memory until
265
+ the context is lost, but never keeps the image itself alive.
266
+
267
+ A meshed skeleton samples the page bitmap every frame, so its decoded form stays
268
+ in use: a floor on the order of `page width × page height × 4` bytes per page —
269
+ 1 MiB for spineboy's single 1024×256 page. A rigid-only skeleton draws nothing
270
+ from the page after `unpackRegions`, and what a merely reachable, undrawn image
271
+ costs is up to the browser, not measurable from script.
272
+
273
+ Unload in this order:
274
+
275
+ 1. `renderer.dispose()`, for every renderer using the images.
276
+ 2. `revokeRegions(regionImages)` (or `assets.dispose()`).
277
+ 3. Drop your references to the atlas and the skeleton data.
278
+
184
279
  ### One part per page (loose part PNGs)
185
280
 
186
281
  Not every pipeline runs the Spine editor's texture packer. If your parts are
@@ -227,10 +322,28 @@ const renderer = new SpineHtmlRenderer(rootElement, regionImages);
227
322
  raster matches the screen: `devicePixelRatio * rootScale`). **Writing it
228
323
  reallocates every mesh canvas backing store on the next frame**, and each
229
324
  reallocation recreates a GPU surface — the cost that took real Safari to ~3 fps
230
- when it happened per frame. Set it when a layout settles, never per frame:
325
+ when it happened per frame. Each canvas is then sized from what the new ratio
326
+ needs, in both directions: lowering the ratio gives the backing pixels back
327
+ (the backing is grow-only *within* a ratio, not across a change of one). Set
328
+ it when a layout settles, never per frame:
231
329
  debounce resize drags and quantize the value instead of tracking it
232
330
  continuously. `renderer.canvasReallocCount` is the check — it must fall back to
233
331
  zero within a second or two.
332
+ - `renderer.syncPixelRatio()` — measures the root's effective on-screen scale
333
+ and sets `pixelRatio` to `devicePixelRatio × scale`, returning the ratio now
334
+ in effect. It appends a hidden 100 px box to the root, reads its box once and
335
+ removes it (the root itself is usually 0×0), so it is **one forced layout per
336
+ call** — which is exactly why the renderer never calls it for you: there is no
337
+ per-frame layout read anywhere in this library. Call it when a zoom or a
338
+ layout *settles* (gesture end, debounced resize), not during the drag. A
339
+ change under 0.1% is ignored, so layout jitter cannot churn GPU surfaces, and
340
+ a root that is not laid out (a `display: none` ancestor) leaves the ratio
341
+ alone. Under an ancestor rotation the measured box is inflated and the ratio
342
+ errs high — oversampling costs pixels, undersampling costs picture.
343
+ - `renderer.meshBackingPixels` — allocated mesh-canvas backing pixels
344
+ (Σ width × height), computed on demand. This is what `pixelRatio` moves
345
+ quadratically, and the cheapest way to see an oversampling stage; the demo
346
+ prints it in the stats line as `backing N Mpx`.
234
347
  - `renderer.meshBackend` — `'canvas2d'` (default) or `'webgl'`; same output, but
235
348
  heavy deforming scenes on Safari want `'webgl'` (see Measured above). Falls back
236
349
  to canvas2d automatically when WebGL is unavailable. Switching re-rasters every
@@ -238,6 +351,22 @@ const renderer = new SpineHtmlRenderer(rootElement, regionImages);
238
351
  - `renderer.triangleExpand` — clip overdraw in px that closes antialiased mesh
239
352
  seams (default 0.5). Also re-rasters every mesh once when changed.
240
353
 
354
+ **A zoomable stage.** The mesh tier rasters at `world × pixelRatio` in the
355
+ root's own coordinates, and it cannot see a CSS transform above the root — so
356
+ the most natural pan/zoom stage, `transform: scale(zoom)` on an ancestor, makes
357
+ it oversample by `1/zoom²` in backing pixels until the zoom is folded in. The
358
+ picture stays correct throughout, which is what makes this easy to ship: only
359
+ the allocation and the frame rate move. Call `syncPixelRatio()` when the zoom
360
+ settles, or set `pixelRatio = devicePixelRatio * zoom` yourself. Measured
361
+ on-device (Chromium, dpr 2, stage under `scale(0.25)`, ~2.2 M CSS px on screen):
362
+
363
+ | scene | `pixelRatio` | `meshBackingPixels` | fps |
364
+ | --- | --- | --- | --- |
365
+ | 57 meshes | `devicePixelRatio` (2) | 139.1 Mpx | 14–21 |
366
+ | 57 meshes | `dpr × zoom` (0.5) | 9.1 Mpx | 61 |
367
+ | 92 meshes | `devicePixelRatio` (2) | 141.1 Mpx | 36–39 |
368
+ | 92 meshes | `dpr × zoom` (0.5) | 9.1 Mpx | 60–61 |
369
+
241
370
  ## Demo (this repository)
242
371
 
243
372
  ```bash
@@ -282,6 +411,15 @@ resolving, so blob ownership — every unpacked URL freed, nothing the caller
282
411
  owns touched, nothing stranded by a failed load — is asserted rather than
283
412
  assumed.
284
413
 
414
+ The server is part of the test: a run tests whatever is being served on its
415
+ port. Two checkouts of this repository on one machine (a worktree, a second
416
+ clone) default to the same port 4321 and reuse an existing server, so the
417
+ second run quietly tests the first one's build — green, and about the wrong
418
+ tree. Give each concurrent checkout its own port with `TEST_PORT`
419
+ (`TEST_PORT=4333 bun run test`); setting it also disables server reuse, so a
420
+ busy port fails the run instead of being borrowed. Unset, nothing changes:
421
+ port 4321, reuse unless `CI`.
422
+
285
423
  Standing rule: **headless numbers are never Safari performance evidence** —
286
424
  nothing in the suite asserts timing, and headless-WebKit fps/ms readings do
287
425
  not transfer (software rasterizer, measured up to 28× off real Safari). The
@@ -20,6 +20,13 @@
20
20
  * preserveDrawingBuffer nor cross-frame content is relied on. The buffer
21
21
  * grows quantized and never shrinks, mirroring the per-part backing policy
22
22
  * (reallocating GPU surfaces per frame is a Safari killer).
23
+ *
24
+ * Page textures are the one thing here with a lifetime, and since this object
25
+ * outlives every renderer they are reference-counted: a renderer retains each
26
+ * page it queues a job for and releases them in dispose(), and the last
27
+ * release deletes the GL texture (a later job re-uploads it). The cache is
28
+ * keyed weakly by the page image, so a consumer that never disposes still
29
+ * loses nothing but GPU memory — this object never pins the caller's image.
23
30
  */
24
31
  export interface MeshBlitJob {
25
32
  /** Destination per-part canvas; the blit clears its full backing first. */
@@ -40,10 +47,22 @@ export interface MeshBlitJob {
40
47
  }
41
48
  declare class MeshGlBlitter {
42
49
  lost: boolean;
50
+ /**
51
+ * Page textures created minus deleted, i.e. how many uploads are live right
52
+ * now. Deterministic (no GC in the path), which is what the lifetime tests
53
+ * assert against — see tests/gl-textures.spec.ts.
54
+ */
55
+ liveTextureCount: number;
43
56
  private readonly canvas;
44
57
  private readonly gl;
45
58
  private readonly uResolution;
46
- private readonly textures;
59
+ /**
60
+ * Weak by the page image on purpose: a consumer that forgets dispose()
61
+ * leaks its texture until context loss, but dropping the image still lets
62
+ * the image (and then the WebGLTexture this entry holds) be collected.
63
+ * Reassigned wholesale on context loss, never iterated.
64
+ */
65
+ private textures;
47
66
  private readonly maxSize;
48
67
  private vertexData;
49
68
  /** Per-job packed rect origins, filled by flush(). */
@@ -56,6 +75,29 @@ declare class MeshGlBlitter {
56
75
  * cannot fit) so the caller can rasterize the batch on the canvas2d path.
57
76
  */
58
77
  flush(jobs: MeshBlitJob[]): boolean;
78
+ /**
79
+ * Declares one more user of `page`. The upload itself stays lazy (the first
80
+ * flush that binds the page does it) — this only counts, so that the last
81
+ * release() can free the texture instead of the module holding it forever.
82
+ * Callers retain once per page and hand the same page back exactly once;
83
+ * SpineHtmlRenderer keeps that ledger and settles it in dispose().
84
+ */
85
+ retain(page: HTMLImageElement): void;
86
+ /**
87
+ * Gives back one retain(). The GL texture goes when the last user of the
88
+ * page is gone; a later job for the same page simply uploads it again. A
89
+ * page the cache does not know (a release after a context loss dropped
90
+ * everything, or a double dispose) is a no-op, so this is safe to call
91
+ * unconditionally.
92
+ */
93
+ release(page: HTMLImageElement): void;
94
+ /**
95
+ * Context loss invalidates every texture handle at once. Drop the cache and
96
+ * the count instead of carrying dead handles (deleting them is pointless —
97
+ * the GPU side is already gone) and let the next frames re-upload if the
98
+ * caller keeps drawing; the renderer meanwhile falls back to canvas2d.
99
+ */
100
+ private markLost;
59
101
  private compile;
60
102
  private textureFor;
61
103
  }
@@ -1 +1 @@
1
- {"version":3,"file":"MeshGlBlitter.d.ts","sourceRoot":"","sources":["../src/MeshGlBlitter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,MAAM,WAAW,WAAW;IAC1B,2EAA2E;IAC3E,MAAM,EAAE,iBAAiB,CAAC;IAC1B,+EAA+E;IAC/E,IAAI,EAAE,gBAAgB,CAAC;IACvB,mFAAmF;IACnF,QAAQ,EAAE,YAAY,CAAC;IACvB,mDAAmD;IACnD,GAAG,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;IACvB,gDAAgD;IAChD,SAAS,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;IAC7B,uCAAuC;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,iFAAiF;IACjF,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AA4BD,cAAM,aAAa;IACjB,IAAI,UAAS;IAEb,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoC;IAC3D,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAwB;IAC3C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAuB;IACnD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6C;IACtE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,UAAU,CAA0B;IAC5C,sDAAsD;IACtD,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,KAAK,CAAgB;;IAoD7B;;;;OAIG;IACH,KAAK,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,OAAO;IAwGnC,OAAO,CAAC,OAAO;IAYf,OAAO,CAAC,UAAU;CAmBnB;AAID;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,aAAa,GAAG,IAAI,CASvD"}
1
+ {"version":3,"file":"MeshGlBlitter.d.ts","sourceRoot":"","sources":["../src/MeshGlBlitter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,MAAM,WAAW,WAAW;IAC1B,2EAA2E;IAC3E,MAAM,EAAE,iBAAiB,CAAC;IAC1B,+EAA+E;IAC/E,IAAI,EAAE,gBAAgB,CAAC;IACvB,mFAAmF;IACnF,QAAQ,EAAE,YAAY,CAAC;IACvB,mDAAmD;IACnD,GAAG,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;IACvB,gDAAgD;IAChD,SAAS,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;IAC7B,uCAAuC;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,iFAAiF;IACjF,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAoCD,cAAM,aAAa;IACjB,IAAI,UAAS;IACb;;;;OAIG;IACH,gBAAgB,SAAK;IAErB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoC;IAC3D,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAwB;IAC3C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAuB;IACnD;;;;;OAKG;IACH,OAAO,CAAC,QAAQ,CAAgD;IAChE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,UAAU,CAA0B;IAC5C,sDAAsD;IACtD,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,KAAK,CAAgB;;IAoD7B;;;;OAIG;IACH,KAAK,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,OAAO;IAwGnC;;;;;;OAMG;IACH,MAAM,CAAC,IAAI,EAAE,gBAAgB,GAAG,IAAI;IAMpC;;;;;;OAMG;IACH,OAAO,CAAC,IAAI,EAAE,gBAAgB,GAAG,IAAI;IAarC;;;;;OAKG;IACH,OAAO,CAAC,QAAQ;IAMhB,OAAO,CAAC,OAAO;IAYf,OAAO,CAAC,UAAU;CA6BnB;AAID;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,aAAa,GAAG,IAAI,CASvD"}
@@ -20,6 +20,13 @@
20
20
  * preserveDrawingBuffer nor cross-frame content is relied on. The buffer
21
21
  * grows quantized and never shrinks, mirroring the per-part backing policy
22
22
  * (reallocating GPU surfaces per frame is a Safari killer).
23
+ *
24
+ * Page textures are the one thing here with a lifetime, and since this object
25
+ * outlives every renderer they are reference-counted: a renderer retains each
26
+ * page it queues a job for and releases them in dispose(), and the last
27
+ * release deletes the GL texture (a later job re-uploads it). The cache is
28
+ * keyed weakly by the page image, so a consumer that never disposes still
29
+ * loses nothing but GPU memory — this object never pins the caller's image.
23
30
  */
24
31
  const VERTEX_SHADER = `
25
32
  attribute vec2 aPos;
@@ -46,10 +53,22 @@ const GUTTER = 1;
46
53
  const GROW_STEP = 256;
47
54
  class MeshGlBlitter {
48
55
  lost = false;
56
+ /**
57
+ * Page textures created minus deleted, i.e. how many uploads are live right
58
+ * now. Deterministic (no GC in the path), which is what the lifetime tests
59
+ * assert against — see tests/gl-textures.spec.ts.
60
+ */
61
+ liveTextureCount = 0;
49
62
  canvas = document.createElement('canvas');
50
63
  gl;
51
64
  uResolution;
52
- textures = new Map();
65
+ /**
66
+ * Weak by the page image on purpose: a consumer that forgets dispose()
67
+ * leaks its texture until context loss, but dropping the image still lets
68
+ * the image (and then the WebGLTexture this entry holds) be collected.
69
+ * Reassigned wholesale on context loss, never iterated.
70
+ */
71
+ textures = new WeakMap();
53
72
  maxSize;
54
73
  vertexData = new Float32Array(8192);
55
74
  /** Per-job packed rect origins, filled by flush(). */
@@ -70,7 +89,7 @@ class MeshGlBlitter {
70
89
  // No restore attempt: on loss the renderer falls back to canvas2d and the
71
90
  // backend signature re-dirties every mesh, so frames stay complete.
72
91
  this.canvas.addEventListener('webglcontextlost', () => {
73
- this.lost = true;
92
+ this.markLost();
74
93
  });
75
94
  const program = gl.createProgram();
76
95
  if (!program)
@@ -194,7 +213,7 @@ class MeshGlBlitter {
194
213
  // If the context died mid-batch the draws above were no-ops; report it
195
214
  // before blitting stale/blank rects onto the part canvases.
196
215
  if (gl.isContextLost()) {
197
- this.lost = true;
216
+ this.markLost();
198
217
  return false;
199
218
  }
200
219
  // Blit each rect onto its per-part canvas: same-task read (no
@@ -211,6 +230,54 @@ class MeshGlBlitter {
211
230
  }
212
231
  return true;
213
232
  }
233
+ /**
234
+ * Declares one more user of `page`. The upload itself stays lazy (the first
235
+ * flush that binds the page does it) — this only counts, so that the last
236
+ * release() can free the texture instead of the module holding it forever.
237
+ * Callers retain once per page and hand the same page back exactly once;
238
+ * SpineHtmlRenderer keeps that ledger and settles it in dispose().
239
+ */
240
+ retain(page) {
241
+ const entry = this.textures.get(page);
242
+ if (entry)
243
+ entry.users++;
244
+ else
245
+ this.textures.set(page, { texture: null, users: 1 });
246
+ }
247
+ /**
248
+ * Gives back one retain(). The GL texture goes when the last user of the
249
+ * page is gone; a later job for the same page simply uploads it again. A
250
+ * page the cache does not know (a release after a context loss dropped
251
+ * everything, or a double dispose) is a no-op, so this is safe to call
252
+ * unconditionally.
253
+ */
254
+ release(page) {
255
+ const entry = this.textures.get(page);
256
+ if (!entry)
257
+ return;
258
+ if (--entry.users > 0)
259
+ return;
260
+ this.textures.delete(page);
261
+ if (!entry.texture)
262
+ return;
263
+ // deleteTexture is a documented no-op on a lost context rather than a
264
+ // throw, but a lost context has already dropped the cache and zeroed the
265
+ // count — reaching here with `lost` set would mean decrementing twice.
266
+ if (!this.lost && !this.gl.isContextLost())
267
+ this.gl.deleteTexture(entry.texture);
268
+ this.liveTextureCount--;
269
+ }
270
+ /**
271
+ * Context loss invalidates every texture handle at once. Drop the cache and
272
+ * the count instead of carrying dead handles (deleting them is pointless —
273
+ * the GPU side is already gone) and let the next frames re-upload if the
274
+ * caller keeps drawing; the renderer meanwhile falls back to canvas2d.
275
+ */
276
+ markLost() {
277
+ this.lost = true;
278
+ this.textures = new WeakMap();
279
+ this.liveTextureCount = 0;
280
+ }
214
281
  compile(type, source) {
215
282
  const gl = this.gl;
216
283
  const shader = gl.createShader(type);
@@ -224,11 +291,11 @@ class MeshGlBlitter {
224
291
  return shader;
225
292
  }
226
293
  textureFor(page) {
227
- let texture = this.textures.get(page);
228
- if (texture)
229
- return texture;
294
+ let entry = this.textures.get(page);
295
+ if (entry?.texture)
296
+ return entry.texture;
230
297
  const gl = this.gl;
231
- texture = gl.createTexture();
298
+ const texture = gl.createTexture();
232
299
  if (!texture)
233
300
  throw new Error('createTexture failed');
234
301
  gl.bindTexture(gl.TEXTURE_2D, texture);
@@ -241,7 +308,17 @@ class MeshGlBlitter {
241
308
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
242
309
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
243
310
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
244
- this.textures.set(page, texture);
311
+ if (!entry) {
312
+ // Nobody retained this page — not a path this package takes, since the
313
+ // renderer retains as it queues each job. Cache it anyway so the batch
314
+ // does not re-upload per flush; with no users only a context loss (or
315
+ // the image being collected, which takes the weak entry with it) clears
316
+ // it, which is exactly the old behavior for a caller that opts out.
317
+ entry = { texture: null, users: 0 };
318
+ this.textures.set(page, entry);
319
+ }
320
+ entry.texture = texture;
321
+ this.liveTextureCount++;
245
322
  return texture;
246
323
  }
247
324
  }
@@ -1 +1 @@
1
- {"version":3,"file":"MeshGlBlitter.js","sourceRoot":"","sources":["../src/MeshGlBlitter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"MeshGlBlitter.js","sourceRoot":"","sources":["../src/MeshGlBlitter.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { type Skeleton } from '@esotericsoftware/spine-core';
2
- import type { RegionImage } from './DomTexture';
2
+ import type { RegionImage } from './DomTexture.js';
3
3
  /** Rasterizer used for the mesh (deform) tier. */
4
4
  export type MeshBackend = 'canvas2d' | 'webgl';
5
5
  /**
@@ -55,7 +55,8 @@ export declare class SpineHtmlRenderer {
55
55
  * Mesh-canvas backing-store pixels per CSS pixel. Defaults to the device
56
56
  * pixel ratio. If the caller scales the root element, fold that scale in
57
57
  * (e.g. devicePixelRatio * rootScale) so the raster matches the on-screen
58
- * resolution instead of over- or under-sampling.
58
+ * resolution instead of over- or under-sampling — syncPixelRatio() measures
59
+ * that scale and folds it in for you.
59
60
  */
60
61
  pixelRatio: number;
61
62
  /**
@@ -78,6 +79,12 @@ export declare class SpineHtmlRenderer {
78
79
  private readonly views;
79
80
  private readonly pendingJobs;
80
81
  private readonly pendingViews;
82
+ /**
83
+ * Atlas page images this renderer has retained from the shared blitter,
84
+ * one retain each. Allocated on the first webgl mesh job, so a renderer
85
+ * that never leaves canvas2d holds nothing and gives nothing back.
86
+ */
87
+ private glPages;
81
88
  private scratchVertices;
82
89
  private tintDefs;
83
90
  /**
@@ -87,23 +94,92 @@ export declare class SpineHtmlRenderer {
87
94
  * @param regionImages Unpacked per-region bitmaps (for the rigid tier).
88
95
  */
89
96
  constructor(root: HTMLElement, regionImages: Map<string, RegionImage>);
97
+ /**
98
+ * Backing-store pixels currently allocated across the mesh canvases
99
+ * (Σ width × height). Computed on demand from the views — nothing is
100
+ * tracked per frame — so reading it is cheap but not free; the demo reads it
101
+ * with the other counters, twice a second.
102
+ *
103
+ * This is the quantity `pixelRatio` moves quadratically, and the one that
104
+ * makes an oversampling stage visible before the frame rate does: a root
105
+ * scaled down by CSS without that scale folded into `pixelRatio` holds
106
+ * 1/scale² of the backing it needs. Hidden slots are included — their
107
+ * canvases keep their allocation.
108
+ */
109
+ get meshBackingPixels(): number;
110
+ /**
111
+ * Re-derives `pixelRatio` from the root's effective on-screen scale, so the
112
+ * mesh tier rasters at the resolution the screen actually shows.
113
+ *
114
+ * Mesh canvases are sized in the root's own coordinates, and a CSS transform
115
+ * anywhere above the root — the natural way to build a pan/zoom stage is
116
+ * `transform: scale(zoom)` on an ancestor — rescales them on screen without
117
+ * the renderer seeing it. At zoom z the raster is then oversampled by 1/z per
118
+ * axis, 1/z² in backing pixels: GPU memory and fill rate, not correctness.
119
+ * This folds the measured scale in, `devicePixelRatio × scale`.
120
+ *
121
+ * One layout read per call. The root itself is usually 0×0 (slot elements are
122
+ * absolutely positioned and posed by transforms), so there is no box to
123
+ * measure: a hidden 100×100 px child is appended, measured once with
124
+ * getBoundingClientRect(), and removed again — nothing of it is left behind.
125
+ * It cannot disturb the slot elements either: they interleave by z-index,
126
+ * which applyCommon() writes explicitly on every visible one, so DOM order is
127
+ * not what orders them; and the probe is `visibility: hidden` and gone before
128
+ * anything can paint.
129
+ *
130
+ * The scale is the larger axis of the measured box. An ancestor rotation
131
+ * inflates that axis-aligned box, so the ratio errs high — the safe side:
132
+ * oversampling costs pixels, undersampling costs picture.
133
+ *
134
+ * A move smaller than 0.1% (see PIXEL_RATIO_DEADBAND) changes nothing, and a
135
+ * root that is not laid out (a `display: none` ancestor — the probe measures
136
+ * 0) changes nothing either. Both matter because writing `pixelRatio`
137
+ * reallocates every mesh canvas on the next frame.
138
+ *
139
+ * The renderer never calls this itself. It is a forced synchronous layout,
140
+ * which has no business in a render path — and there is no per-frame layout
141
+ * read anywhere in this class. Call it when a zoom or a layout settles: after
142
+ * a wheel/pinch gesture ends, on a debounced resize, not during the drag.
143
+ *
144
+ * @returns the pixel ratio now in effect (the current one if nothing moved).
145
+ */
146
+ syncPixelRatio(): number;
90
147
  render(skeleton: Skeleton): void;
91
148
  /**
92
149
  * Removes every element this renderer added to the root (slot elements and
93
- * the tint filter defs). The region bitmaps are deliberately untouched: the
94
- * map is the caller's, and one map is normally shared by many renderers
95
- * (disposing one instance must not blind the others). Free the unpacked
96
- * blob URLs with revokeRegions() once no renderer needs them.
150
+ * the tint filter defs), and hands the atlas pages it uploaded back to the
151
+ * shared GL blitter the one resource here that outlives the instance, so
152
+ * the one that has to be given back explicitly (GPU memory, and no garbage
153
+ * collector feels pressure from it). The pages are reference-counted there:
154
+ * a page another live renderer still draws stays uploaded, and the texture
155
+ * is deleted only when the last user of it is disposed.
156
+ *
157
+ * The region bitmaps are deliberately untouched: the map is the caller's,
158
+ * and one map is normally shared by many renderers (disposing one instance
159
+ * must not blind the others). Free the unpacked blob URLs with
160
+ * revokeRegions() once no renderer needs them.
161
+ *
162
+ * Idempotent: the retained-page ledger is cleared here, so a second call
163
+ * releases nothing a second time.
97
164
  */
98
165
  dispose(): void;
99
166
  private renderRegion;
100
167
  private renderMesh;
168
+ /**
169
+ * Declares this renderer a user of `page` with the shared blitter, once per
170
+ * page — the counterpart of the release in dispose(). The set is this
171
+ * renderer's own ledger, so sharing one page across renderers works by
172
+ * counting rather than by guessing. On the render path this costs one
173
+ * Set.has per queued mesh job and allocates nothing after the first page.
174
+ */
175
+ private retainPage;
101
176
  /** The canvas2d raster path: clear the backing, map each triangle. */
102
177
  private rasterizeMesh2d;
103
178
  /**
104
179
  * Standard canvas triangle texture mapping (same math as the official
105
180
  * spine-canvas renderer): derive the affine that sends the triangle's
106
- * texture-space corners to its screen-space corners, clip, draw the page.
181
+ * texture-space corners to its screen-space corners, clip, draw the page
182
+ * but only the part of the page this triangle can show (see below).
107
183
  *
108
184
  * The clip polygon is expanded outward from the centroid by a fraction of
109
185
  * a pixel. Browsers that antialias clip paths (Safari) otherwise leave
@@ -112,6 +188,44 @@ export declare class SpineHtmlRenderer {
112
188
  * across the shared edge the overlap draws the same pixels — cracks close
113
189
  * with no visible cost. The texture-mapping affine itself stays exact.
114
190
  *
191
+ * ## Why a source sub-rect instead of the whole page
192
+ *
193
+ * The draw is a 9-argument `drawImage` whose source rect covers exactly what
194
+ * the clip can reveal, placed at the same texture-space position, so the
195
+ * mapping is identical to drawing the whole page — the same affine, the same
196
+ * texels, only fewer of them offered to the rasterizer.
197
+ *
198
+ * That is not an optimization. Linux WebKit — the software rasterizer, the
199
+ * only engine this has been observed on; Chromium and macOS WebKit never show
200
+ * it — garbles individual triangles when the whole page is drawn under a
201
+ * steep per-triangle affine: read off the parity dump, head/goggles/foot
202
+ * triangles land with displaced texture while the webgl capture of the same
203
+ * pose stays clean. Switching this one call to a source sub-rect — the only
204
+ * variable changed — took that platform's bad-pixel counts from 1042 to 2
205
+ * (hoverboard), 231 to 4 (portal) and 803 to 3 (walk + tint), and left every
206
+ * Chromium capture byte-identical. [measured on the ubuntu CI runner through
207
+ * PARITY_DUMP: first with a fixed 2-texel pad (run 35209262693), then with
208
+ * the derived rect below — the same counts (run 35213286993).] WHY that
209
+ * rasterizer garbles the whole-page form is not identified; steep affines
210
+ * making whole-page coordinates large is a suspicion, not a measurement. The
211
+ * sub-rect sidesteps it. (Moving `clip()` before `transform()` was tried as a
212
+ * separate one-variable experiment: every number came back identical, so the
213
+ * clip path is not the cause.)
214
+ *
215
+ * ## How the sub-rect is derived
216
+ *
217
+ * It must provably cover everything the clip can reveal, which is the
218
+ * *expanded* polygon, not the triangle. So each clip corner's canvas-space
219
+ * displacement is pushed back through the inverse of the 2×2 affine into
220
+ * texture space, the bounding box is taken over the displaced corners, one
221
+ * texel of bilinear support is added, and the result is floor/ceil'd to texel
222
+ * boundaries (so the sub-rect itself resamples nothing) and clamped to the
223
+ * page. A fixed pad would not do: the texture-space reach of the expansion is
224
+ * `triangleExpand` times the local texels-per-canvas-unit, which on the
225
+ * demo's own meshes ranges from ~0.26 texels on a typical triangle to ~8.6 on
226
+ * a foreshortened one. Under-padding lets the rim of the overdraw sample
227
+ * nothing, which brings back the very seams the overdraw exists to close.
228
+ *
115
229
  * No half-texel offset is applied to the incoming u/v: the `(i + 0.5) / size`
116
230
  * texel-centre convention belongs to *sampling* (landing inside the intended
117
231
  * texel instead of on a filter boundary). This solves for the affine that
@@ -1 +1 @@
1
- {"version":3,"file":"SpineHtmlRenderer.d.ts","sourceRoot":"","sources":["../src/SpineHtmlRenderer.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,QAAQ,EAId,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD,kDAAkD;AAClD,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,OAAO,CAAC;AAiE/C;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,qBAAa,iBAAiB;IA2D1B,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,YAAY;IA3D/B,kEAAkE;IAClE,aAAa,SAAK;IAClB,2CAA2C;IAC3C,SAAS,SAAK;IACd,kEAAkE;IAClE,cAAc,SAAK;IACnB;;;;OAIG;IACH,kBAAkB,SAAK;IACvB,uCAAuC;IACvC,aAAa,SAAK;IAClB;;;;OAIG;IACH,cAAc,SAAO;IACrB;;;;;OAKG;IACH,UAAU,SAA+D;IACzE;;;;;;;;;;;;;OAaG;IACH,WAAW,EAAE,WAAW,CAAc;IACtC,+EAA+E;IAC/E,iBAAiB,EAAE,WAAW,CAAc;IAE5C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA6B;IACnD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqB;IACjD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAkB;IAC/C,OAAO,CAAC,eAAe,CAAyB;IAChD,OAAO,CAAC,QAAQ,CAA8B;IAE9C;;;;;OAKG;gBAEgB,IAAI,EAAE,WAAW,EACjB,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC;IAGzD,MAAM,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI;IA4ChC;;;;;;OAMG;IACH,OAAO,IAAI,IAAI;IASf,OAAO,CAAC,YAAY;IA8CpB,OAAO,CAAC,UAAU;IAsIlB,sEAAsE;IACtE,OAAO,CAAC,eAAe;IAqCvB;;;;;;;;;;;;;;;;;;;OAmBG;IACH,OAAO,CAAC,YAAY;IAwCpB,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,WAAW;IAmDnB,OAAO,CAAC,aAAa;IAyBrB,OAAO,CAAC,IAAI;IA+CZ,OAAO,CAAC,IAAI;CAOb"}
1
+ {"version":3,"file":"SpineHtmlRenderer.d.ts","sourceRoot":"","sources":["../src/SpineHtmlRenderer.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,QAAQ,EAId,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAGnD,kDAAkD;AAClD,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,OAAO,CAAC;AA4E/C;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,qBAAa,iBAAiB;IAkE1B,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,YAAY;IAlE/B,kEAAkE;IAClE,aAAa,SAAK;IAClB,2CAA2C;IAC3C,SAAS,SAAK;IACd,kEAAkE;IAClE,cAAc,SAAK;IACnB;;;;OAIG;IACH,kBAAkB,SAAK;IACvB,uCAAuC;IACvC,aAAa,SAAK;IAClB;;;;OAIG;IACH,cAAc,SAAO;IACrB;;;;;;OAMG;IACH,UAAU,SAA+D;IACzE;;;;;;;;;;;;;OAaG;IACH,WAAW,EAAE,WAAW,CAAc;IACtC,+EAA+E;IAC/E,iBAAiB,EAAE,WAAW,CAAc;IAE5C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA6B;IACnD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqB;IACjD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAkB;IAC/C;;;;OAIG;IACH,OAAO,CAAC,OAAO,CAAsC;IACrD,OAAO,CAAC,eAAe,CAAyB;IAChD,OAAO,CAAC,QAAQ,CAA8B;IAE9C;;;;;OAKG;gBAEgB,IAAI,EAAE,WAAW,EACjB,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC;IAGzD;;;;;;;;;;;OAWG;IACH,IAAI,iBAAiB,IAAI,MAAM,CAM9B;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAmCG;IACH,cAAc,IAAI,MAAM;IAiCxB,MAAM,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI;IA4ChC;;;;;;;;;;;;;;;;OAgBG;IACH,OAAO,IAAI,IAAI;IAcf,OAAO,CAAC,YAAY;IA8CpB,OAAO,CAAC,UAAU;IA+IlB;;;;;;OAMG;IACH,OAAO,CAAC,UAAU;IAWlB,sEAAsE;IACtE,OAAO,CAAC,eAAe;IAqCvB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA0DG;IACH,OAAO,CAAC,YAAY;IA8EpB,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,WAAW;IAmDnB,OAAO,CAAC,aAAa;IAyBrB,OAAO,CAAC,IAAI;IA+CZ,OAAO,CAAC,IAAI;CAOb"}