@displayxr/inline3d 1.47.5 → 1.47.6
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/CHANGELOG.md +62 -0
- package/README.md +7 -1
- package/dist/call.js +1 -1
- package/docs/content-lifecycle-rules.md +270 -0
- package/js/inline3d-model-entry.js +3 -0
- package/js/inline3d-model-playcanvas.js +36 -2
- package/js/inline3d-pc-auto-release.js +191 -0
- package/js/inline3d-pc-release.js +107 -0
- package/js/inline3d-pc-shader-cache.js +172 -0
- package/js/inline3d-playcanvas-engine.js +2 -1
- package/js/inline3d-splat-deferred.js +2 -0
- package/js/inline3d-splat-diag.js +125 -4
- package/js/inline3d-splat-playcanvas.js +40 -3
- package/js/inline3d-splat.js +2 -0
- package/llms.txt +1 -0
- package/model.d.ts +12 -0
- package/package.json +5 -1
- package/splat.d.ts +12 -0
|
@@ -34,6 +34,19 @@
|
|
|
34
34
|
// located them, and a live outgoing photo goes back on the pre-1.24 node chain (which
|
|
35
35
|
// scales its disparity by the two rigs' window ratio at the swap). A/B for "the
|
|
36
36
|
// outgoing photo jumps when the incoming rig is declared".
|
|
37
|
+
// keepshaders — leave the engine's compiled-shader cache as it ships (no per-program release,
|
|
38
|
+
// ./inline3d-pc-shader-cache.js). Page-wide: the engine's shader class is shared.
|
|
39
|
+
// keepprograms — a material the page destroys keeps the programs it linked, as the engine ships
|
|
40
|
+
// (./inline3d-pc-auto-release.js). Per tile.
|
|
41
|
+
//
|
|
42
|
+
// LIVE GL OBJECTS (docs/content-lifecycle-rules.md): programs and shaders created / deleted on the
|
|
43
|
+
// tile's context since diag attached, and the live difference, on the overlay and in the dump.
|
|
44
|
+
// Sampled at every content swap the SDK can see: each setSource, each glTF container the tile's
|
|
45
|
+
// asset registry loads (a page swapping meshes under handle.engine.root), each
|
|
46
|
+
// handle.releaseEntity. When the live program count has not dropped across the last LEAK_SWAPS
|
|
47
|
+
// swaps of one kind and rose at more than 60% of them, one console warning says so. A page that
|
|
48
|
+
// releases what a swap replaces goes up and down, or plateaus once its own transitions are
|
|
49
|
+
// compiled (a few rises, then flat); one that leaks climbs.
|
|
37
50
|
//
|
|
38
51
|
// Per frame, `rigAt` / `rigIn` / `rigOut` say which declared rig the views were located for
|
|
39
52
|
// (the tracker's id; null = none matched), which rig the current photo was drawn through
|
|
@@ -52,8 +65,28 @@
|
|
|
52
65
|
// - the settle itself: the SDK's settle work in ms, then how long the SAME task kept running
|
|
53
66
|
// after it (the page's continuation of `await setSource`).
|
|
54
67
|
|
|
68
|
+
import { autoReleaseStats, autoReleaseState } from './inline3d-pc-auto-release.js';
|
|
69
|
+
|
|
55
70
|
/** The switches `diag` / `?dxrdiag` understand, besides the plain on values. */
|
|
56
|
-
export const DIAG_SWITCHES = Object.freeze(['norig', 'frozen', 'nowarm', 'cold', 'nooverlay', 'oldpick', 'nolayerrig', 'oldrig']);
|
|
71
|
+
export const DIAG_SWITCHES = Object.freeze(['norig', 'frozen', 'nowarm', 'cold', 'nooverlay', 'oldpick', 'nolayerrig', 'oldrig', 'keepshaders', 'keepprograms']);
|
|
72
|
+
/**
|
|
73
|
+
* The leak watch's window: content swaps of one kind with no drop in live programs, more than 60%
|
|
74
|
+
* of them a rise. Wide enough that a first pass through the transitions (each compiles its own
|
|
75
|
+
* programs once: four rises for cut / crossfade / wavefront / flip) does not trip it.
|
|
76
|
+
*/
|
|
77
|
+
export const LEAK_SWAPS = 10;
|
|
78
|
+
|
|
79
|
+
/** The leak watch's rule on one kind's samples (oldest first): the warning's condition. */
|
|
80
|
+
export function looksLikeLeak(samples, n = LEAK_SWAPS) {
|
|
81
|
+
if (samples.length < n + 1) return false;
|
|
82
|
+
const w = samples.slice(-(n + 1));
|
|
83
|
+
let rises = 0;
|
|
84
|
+
for (let i = 1; i < w.length; i++) {
|
|
85
|
+
if (w[i] < w[i - 1]) return false;
|
|
86
|
+
if (w[i] > w[i - 1]) rises++;
|
|
87
|
+
}
|
|
88
|
+
return rises > n * 0.6;
|
|
89
|
+
}
|
|
57
90
|
const ON_TOKENS = new Set(['1', 'on', 'true', 'yes']);
|
|
58
91
|
|
|
59
92
|
/**
|
|
@@ -207,6 +240,12 @@ export class DiagRecorder {
|
|
|
207
240
|
this._listeners = new Set();
|
|
208
241
|
this._lafo = null;
|
|
209
242
|
this._gl = null; // the wrapped context + its originals, for dispose()
|
|
243
|
+
/** GL object counts on the tile's context (instrumentGl): programs / shaders created + deleted. */
|
|
244
|
+
this.glObjects = { programs: { created: 0, deleted: 0 }, shaders: { created: 0, deleted: 0 } };
|
|
245
|
+
this.shaderCacheState = null; // () => the engine shader-cache patch state (./inline3d-pc-shader-cache.js)
|
|
246
|
+
this.programReleaseState = null; // () => { state, released, … } (./inline3d-pc-auto-release.js)
|
|
247
|
+
this._swapLive = new Map(); // kind → live programs at each content swap of that kind (the leak watch)
|
|
248
|
+
this._leakWarned = false;
|
|
210
249
|
this._burst = null; // the pick burst of the current task
|
|
211
250
|
}
|
|
212
251
|
|
|
@@ -266,6 +305,25 @@ export class DiagRecorder {
|
|
|
266
305
|
*/
|
|
267
306
|
instrumentGl(gl) {
|
|
268
307
|
if (!gl || this._gl) return;
|
|
308
|
+
// Object counts first (no timing): what a content swap leaves behind.
|
|
309
|
+
const objs = this.glObjects;
|
|
310
|
+
const counters = {
|
|
311
|
+
createProgram: (r) => r && objs.programs.created++,
|
|
312
|
+
deleteProgram: (r, a) => a[0] && objs.programs.deleted++,
|
|
313
|
+
createShader: (r) => r && objs.shaders.created++,
|
|
314
|
+
deleteShader: (r, a) => a[0] && objs.shaders.deleted++,
|
|
315
|
+
};
|
|
316
|
+
const counted = [];
|
|
317
|
+
for (const [name, count] of Object.entries(counters)) {
|
|
318
|
+
const orig = gl[name];
|
|
319
|
+
if (typeof orig !== 'function') continue;
|
|
320
|
+
gl[name] = function (...a) {
|
|
321
|
+
const r = orig.apply(this, a);
|
|
322
|
+
count(r, a);
|
|
323
|
+
return r;
|
|
324
|
+
};
|
|
325
|
+
counted.push(name);
|
|
326
|
+
}
|
|
269
327
|
const LINK = 0x8b82;
|
|
270
328
|
const COMPLETION = 0x91b1;
|
|
271
329
|
const kinds = {
|
|
@@ -294,7 +352,37 @@ export class DiagRecorder {
|
|
|
294
352
|
};
|
|
295
353
|
saved.push(name);
|
|
296
354
|
}
|
|
297
|
-
this._gl = { gl, saved };
|
|
355
|
+
this._gl = { gl, saved: [...counted, ...saved] };
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/** Live programs / shaders on the tile's context since instrumentGl (created − deleted). */
|
|
359
|
+
liveGl() {
|
|
360
|
+
const { programs: p, shaders: s } = this.glObjects;
|
|
361
|
+
return { programs: p.created - p.deleted, shaders: s.created - s.deleted };
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* A content swap happened (`kind`: 'setSource' at its start, 'container' at a glTF load,
|
|
366
|
+
* 'release' after a releaseEntity): sample the live program count, and warn once when the
|
|
367
|
+
* samples of that kind look like a leak (looksLikeLeak).
|
|
368
|
+
*/
|
|
369
|
+
contentSwap(kind) {
|
|
370
|
+
if (!this._gl) return null;
|
|
371
|
+
const live = this.liveGl();
|
|
372
|
+
let w = this._swapLive.get(kind);
|
|
373
|
+
if (!w) this._swapLive.set(kind, (w = []));
|
|
374
|
+
w.push(live.programs);
|
|
375
|
+
if (w.length > LEAK_SWAPS + 1) w.shift();
|
|
376
|
+
this._event('swap', { kind, ...live }, this.now());
|
|
377
|
+
if (!this._leakWarned && looksLikeLeak(w)) {
|
|
378
|
+
this._leakWarned = true;
|
|
379
|
+
console.warn(
|
|
380
|
+
`[inline3d/${this.label.split('#')[0]}] diag: live WebGL programs never dropped across the last ${LEAK_SWAPS} content swaps ` +
|
|
381
|
+
`and rose at most of them (${kind}: ${w.join(' → ')}). Something each swap replaces is not released — see docs/content-lifecycle-rules.md ` +
|
|
382
|
+
'(handle.releaseEntity for content under handle.engine.root). Warned once.',
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
return live;
|
|
298
386
|
}
|
|
299
387
|
|
|
300
388
|
/** One GL call of `kind` that took `ms`, at `t` (instrumentGl's wrappers; tests call it directly). */
|
|
@@ -578,6 +666,7 @@ export class DiagRecorder {
|
|
|
578
666
|
label: this.label,
|
|
579
667
|
switches: [...this.switches],
|
|
580
668
|
rigLocked: this.rigLocked,
|
|
669
|
+
glObjects: { ...this.glObjects, live: this.liveGl(), shaderCache: safe(() => this.shaderCacheState?.()) ?? null, programRelease: safe(() => this.programReleaseState?.()) ?? null },
|
|
581
670
|
transitions: this.transitions,
|
|
582
671
|
events: this.events,
|
|
583
672
|
frames: this.frames,
|
|
@@ -794,7 +883,8 @@ export function startDiagLoop(rec, { overlay = true, doc = typeof document !== '
|
|
|
794
883
|
`dxr-diag ${rec.label} phase ${rec.phase}` +
|
|
795
884
|
(Number.isFinite(rec.callAt) ? ` +${Math.round(now - rec.callAt)} ms since setSource` : '') +
|
|
796
885
|
(rec.switches.size ? ` [${[...rec.switches].join(',')}]` : '') +
|
|
797
|
-
(last ? `\nframe dt ${last.dt} ms held ${last.held ? 'YES' : 'no'} img ${last.img}${last.img !== 'none' ? ' ' + last.imgW : ''}` : '')
|
|
886
|
+
(last ? `\nframe dt ${last.dt} ms held ${last.held ? 'YES' : 'no'} img ${last.img}${last.img !== 'none' ? ' ' + last.imgW : ''}` : '') +
|
|
887
|
+
(rec._gl ? `\n${glObjectsLine(rec)}` : '');
|
|
798
888
|
strip(rec.frames, 0, now - WINDOW_MS, now, rec.callAt);
|
|
799
889
|
if (frozen && g) {
|
|
800
890
|
strip(frozen.window.map((f) => ({ ...f })), STRIP_H + 18, -PRE_MS, WINDOW_MS - PRE_MS, 0);
|
|
@@ -818,6 +908,37 @@ export function startDiagLoop(rec, { overlay = true, doc = typeof document !== '
|
|
|
818
908
|
};
|
|
819
909
|
}
|
|
820
910
|
|
|
911
|
+
/**
|
|
912
|
+
* The live-GL-object watch on a PlayCanvas tile: count the context's programs and shaders, show
|
|
913
|
+
* the engine shader-cache patch state, and sample at the content swaps the SDK can see (a
|
|
914
|
+
* container load in the tile's registry, a releaseEntity). setSource samples itself.
|
|
915
|
+
*/
|
|
916
|
+
export function watchContent(rec, app, viewer) {
|
|
917
|
+
rec.instrumentGl(app?.graphicsDevice?.gl);
|
|
918
|
+
rec.shaderCacheState = () => app?.graphicsDevice?.__dxrShaderCache ?? null;
|
|
919
|
+
rec.programReleaseState = () => {
|
|
920
|
+
const state = app?.graphicsDevice?.__dxrAutoRelease ?? null;
|
|
921
|
+
const st = autoReleaseStats(app);
|
|
922
|
+
return state ? { state, ...(st || {}) } : null;
|
|
923
|
+
};
|
|
924
|
+
const ar = autoReleaseState(app);
|
|
925
|
+
if (ar) ar.onSweep = (n) => n && rec.contentSwap('auto-release');
|
|
926
|
+
if (viewer) viewer.onContentReleased = () => rec.contentSwap('release');
|
|
927
|
+
app?.assets?.on?.('load', (a) => a?.type === 'container' && rec.contentSwap('container'));
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
/** The overlay's GL objects line: live programs and shaders (created / deleted), the cache patch. */
|
|
931
|
+
export function glObjectsLine(rec) {
|
|
932
|
+
const { programs: p, shaders: s } = rec.glObjects;
|
|
933
|
+
const cache = safe(() => rec.shaderCacheState?.());
|
|
934
|
+
const rel = safe(() => rec.programReleaseState?.());
|
|
935
|
+
return (
|
|
936
|
+
`gl programs ${p.created - p.deleted} live (+${p.created} −${p.deleted}) shaders ${s.created - s.deleted} live (+${s.created} −${s.deleted})` +
|
|
937
|
+
(cache ? ` cache ${cache}` : '') +
|
|
938
|
+
(rel ? ` release ${rel.state}${rel.released !== undefined ? ` (−${rel.released})` : ''}` : '')
|
|
939
|
+
);
|
|
940
|
+
}
|
|
941
|
+
|
|
821
942
|
/**
|
|
822
943
|
* `window.__dxrDiag`: every diag tile on the page. `dump()` is the JSON to paste back;
|
|
823
944
|
* `last` the most recent tile's recorder; `transitions` every tile's transition summaries.
|
|
@@ -839,7 +960,7 @@ export function registerDiag(rec) {
|
|
|
839
960
|
},
|
|
840
961
|
};
|
|
841
962
|
}
|
|
842
|
-
rec.label =
|
|
963
|
+
rec.label = `${rec.label || 'splat'}#${g.tiles.length + 1}`;
|
|
843
964
|
g.tiles.push(rec);
|
|
844
965
|
const dispose = rec.dispose.bind(rec);
|
|
845
966
|
rec.dispose = () => {
|
|
@@ -73,7 +73,7 @@ import { viewerEaseFor, frameTrackingState } from './inline3d-viewer-ease.js';
|
|
|
73
73
|
import { VideoPlane, validateSetVideo, PAGE_VIDEO_ERROR, eyeSplit, EYE_SPLIT_UNIFORM, makeSbsMaterial } from './inline3d-splat-video.js';
|
|
74
74
|
import { LayerRigCameras, validateLayerRig, validateLayerRigOptions, DEFAULT_VIEWER_DISTANCE_M } from './inline3d-splat-layer-rig.js';
|
|
75
75
|
import { RigTracker, remapViews, nodePose, sameRig } from './inline3d-splat-rig-map.js';
|
|
76
|
-
import { resolveDiag, DiagRecorder, startDiagLoop, registerDiag, DIAG_SWITCHES } from './inline3d-splat-diag.js';
|
|
76
|
+
import { resolveDiag, DiagRecorder, startDiagLoop, registerDiag, watchContent, DIAG_SWITCHES } from './inline3d-splat-diag.js';
|
|
77
77
|
import {
|
|
78
78
|
glBufferLimits,
|
|
79
79
|
probeBufferLimits,
|
|
@@ -127,6 +127,9 @@ import {
|
|
|
127
127
|
framedSubjectBounds,
|
|
128
128
|
} from './inline3d-splat-shared.js';
|
|
129
129
|
import { resolveCursorOption } from './inline3d-cursor-option.js';
|
|
130
|
+
import { installShaderCacheRelease } from './inline3d-pc-shader-cache.js';
|
|
131
|
+
import { installAutoProgramRelease } from './inline3d-pc-auto-release.js';
|
|
132
|
+
import { releaseEntity as releaseEntityIn } from './inline3d-pc-release.js';
|
|
130
133
|
|
|
131
134
|
/**
|
|
132
135
|
* The component systems the tile's `AppBase` registers. Camera + GSplat draw the splat; Render,
|
|
@@ -1284,6 +1287,18 @@ export class PlayCanvasSplatViewer {
|
|
|
1284
1287
|
return shaders.size;
|
|
1285
1288
|
}
|
|
1286
1289
|
|
|
1290
|
+
/**
|
|
1291
|
+
* Destroy `entity` (content a page added under `engine.root`) and release the programs its
|
|
1292
|
+
* materials linked, plus its glTF container (`asset`) when nothing else draws from it —
|
|
1293
|
+
* ./inline3d-pc-release.js, docs/content-lifecycle-rules.md. Returns { programs, container }.
|
|
1294
|
+
*/
|
|
1295
|
+
releaseEntity(entity, { asset = null } = {}) {
|
|
1296
|
+
if (!this.app || this._disposed) return { programs: 0, container: null };
|
|
1297
|
+
const r = releaseEntityIn(this.app, entity, { asset });
|
|
1298
|
+
this.onContentReleased?.(r);
|
|
1299
|
+
return r;
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1287
1302
|
useSceneColor(on = true) {
|
|
1288
1303
|
this.sceneColor = !!on;
|
|
1289
1304
|
const cams = [this.eye, ...(this._viewPath === 'cameras' ? this._views : [])];
|
|
@@ -1415,7 +1430,7 @@ export class PlayCanvasSplatViewer {
|
|
|
1415
1430
|
* @param {object} o.perf the resolved playcanvasPerfSettings().
|
|
1416
1431
|
* @param {string} [o.viewPath] force 'cameras' | 'renderview' (diagnostics).
|
|
1417
1432
|
*/
|
|
1418
|
-
async attachEngine(pc, { preserveDrawingBuffer = false, perf, viewPath, antialias = false, patchSplats = true } = {}) {
|
|
1433
|
+
async attachEngine(pc, { preserveDrawingBuffer = false, perf, viewPath, antialias = false, patchSplats = true, shaderCacheRelease = true, programRelease = true } = {}) {
|
|
1419
1434
|
this.pc = pc;
|
|
1420
1435
|
const device = await pc.createGraphicsDevice(this.canvas, {
|
|
1421
1436
|
deviceTypes: [pc.DEVICETYPE_WEBGL2],
|
|
@@ -1431,6 +1446,11 @@ export class PlayCanvasSplatViewer {
|
|
|
1431
1446
|
device.destroy?.();
|
|
1432
1447
|
return null;
|
|
1433
1448
|
}
|
|
1449
|
+
// The engine keeps every compiled shader until the device dies, even once its program is
|
|
1450
|
+
// destroyed: count them per program and free the last one out (./inline3d-pc-shader-cache.js).
|
|
1451
|
+
// `?dxrdiag=keepshaders` leaves the engine's cache as it ships (A/B; page-wide, first tile wins).
|
|
1452
|
+
if (shaderCacheRelease) installShaderCacheRelease(device);
|
|
1453
|
+
else device.__dxrShaderCache ??= 'off (keepshaders)';
|
|
1434
1454
|
const opts = new pc.AppOptions();
|
|
1435
1455
|
opts.graphicsDevice = device;
|
|
1436
1456
|
// No xr (AppBase constructs XrManager only when asked, and XrManager is what probes and
|
|
@@ -1442,6 +1462,11 @@ export class PlayCanvasSplatViewer {
|
|
|
1442
1462
|
const app = new pc.AppBase(this.canvas);
|
|
1443
1463
|
app.init(opts);
|
|
1444
1464
|
guardDuplicateSystems(app);
|
|
1465
|
+
// A material the page destroys (entity.destroy, a container's unload) keeps every program it
|
|
1466
|
+
// linked for the device's life: release them a frame later, when nothing else uses them
|
|
1467
|
+
// (./inline3d-pc-auto-release.js). `?dxrdiag=keepprograms` leaves the engine as it ships (A/B).
|
|
1468
|
+
if (programRelease) installAutoProgramRelease(app, pc.Material);
|
|
1469
|
+
else device.__dxrAutoRelease ??= 'off (keepprograms)';
|
|
1445
1470
|
// The SDK sizes the buffer (double-width in 3D, 1:1 in mono), so the engine must never
|
|
1446
1471
|
// resize it. RESOLUTION_FIXED is AppBase's DEFAULT, and with it `updateCanvasSize()` is a
|
|
1447
1472
|
// no-op. Deliberately NOT calling setCanvasResolution/setCanvasFillMode: without explicit
|
|
@@ -3583,6 +3608,14 @@ export function attachPlayCanvasSplat(out, wall, canvas, src, opts, pending = []
|
|
|
3583
3608
|
await first.catch(() => null);
|
|
3584
3609
|
return removed ? 0 : viewer.warmEntity(entity, o);
|
|
3585
3610
|
},
|
|
3611
|
+
/**
|
|
3612
|
+
* Destroy `entity` (content under handle.engine.root) and release the programs its materials
|
|
3613
|
+
* linked, and its container `asset` when nothing else draws from it — viewer.releaseEntity.
|
|
3614
|
+
* Returns { programs, container }.
|
|
3615
|
+
*/
|
|
3616
|
+
releaseEntity(entity, o) {
|
|
3617
|
+
return removed ? { programs: 0, container: null } : viewer.releaseEntity(entity, o);
|
|
3618
|
+
},
|
|
3586
3619
|
setStereo,
|
|
3587
3620
|
setDepthEnvelope,
|
|
3588
3621
|
/**
|
|
@@ -4745,12 +4778,15 @@ export function attachPlayCanvasSplat(out, wall, canvas, src, opts, pending = []
|
|
|
4745
4778
|
perf: perfResolved,
|
|
4746
4779
|
viewPath: opts.playcanvasViewPath,
|
|
4747
4780
|
antialias: antialias === true,
|
|
4781
|
+
shaderCacheRelease: !diag?.has('keepshaders'),
|
|
4782
|
+
programRelease: !diag?.has('keepprograms'),
|
|
4748
4783
|
});
|
|
4749
4784
|
if (!app || removed) return null;
|
|
4750
4785
|
pcModule = pc;
|
|
4751
4786
|
// diag: count the GL calls that can block (compile, link, status queries, readbacks, syncs),
|
|
4752
4787
|
// from before the first asset's compile on
|
|
4753
|
-
|
|
4788
|
+
// + the live program / shader counts and the content-swap leak watch
|
|
4789
|
+
if (diag) watchContent(diag, app, viewer);
|
|
4754
4790
|
/**
|
|
4755
4791
|
* ADVANCED, not covered by the semver promise: the engine objects behind this window.
|
|
4756
4792
|
* `app` is the tile's `pc.AppBase`; `root` the content root, in the splat's content space
|
|
@@ -5335,6 +5371,7 @@ export function attachPlayCanvasSplat(out, wall, canvas, src, opts, pending = []
|
|
|
5335
5371
|
const gen = ++sourceGen;
|
|
5336
5372
|
if (diag) {
|
|
5337
5373
|
rigLockArmed = true;
|
|
5374
|
+
diag.contentSwap('setSource'); // the previous swap's outgoing asset is released by now
|
|
5338
5375
|
diag.setPhase('swap', {
|
|
5339
5376
|
transition: plan.sequence ? `${plan.sequence.name}(${plan.sequence.out.effect}>${plan.sequence.in.effect})` : plan.transition,
|
|
5340
5377
|
outgoing: plan.sequence ? 'none (one splat)' : plan.outgoing || 'default',
|
package/js/inline3d-splat.js
CHANGED
|
@@ -447,6 +447,8 @@ function addSplatUnguarded(wall, canvas, src, opts = {}) {
|
|
|
447
447
|
},
|
|
448
448
|
/** PlayCanvas-backend feature (its renderer links synchronously); nothing to warm here. */
|
|
449
449
|
warmEntity: () => Promise.resolve(0),
|
|
450
|
+
/** PlayCanvas-backend feature: three.js content is the page's to dispose (docs/content-lifecycle-rules.md). */
|
|
451
|
+
releaseEntity: () => ({ programs: 0, container: null }),
|
|
450
452
|
stopEffect() {
|
|
451
453
|
throw effectsNotOnSpark('stopEffect()');
|
|
452
454
|
},
|
package/llms.txt
CHANGED
|
@@ -15,6 +15,7 @@ detail, a cover that always comes off). The SDK's defaults do the rest.
|
|
|
15
15
|
## Reference
|
|
16
16
|
- docs/authoring-inline-3d.md — every API, both view rigs, comfort maths
|
|
17
17
|
- docs/woven-canvas-rules.md — what the browser does to a woven canvas; CSS, remount and cover rules
|
|
18
|
+
- docs/content-lifecycle-rules.md — swapping content in a window: release programs, textures, containers (handle.releaseEntity)
|
|
18
19
|
- docs/porting-three-js-apps.md — moving an existing three.js app over
|
|
19
20
|
- docs/authoring-motion-and-effects.md, docs/splat-effects.md — motion, reveals, depth envelopes
|
|
20
21
|
- index.d.ts, three.d.ts, viewer.d.ts, model.d.ts, splat.d.ts — the public API with JSDoc
|
package/model.d.ts
CHANGED
|
@@ -283,6 +283,18 @@ export interface ModelHandle {
|
|
|
283
283
|
* docs/playcanvas-adapter.md §warmEntity.
|
|
284
284
|
*/
|
|
285
285
|
warmEntity(entity?: unknown, opts?: { timeoutMs?: number }): Promise<number>;
|
|
286
|
+
/**
|
|
287
|
+
* `engine: 'playcanvas'` only (a no-op returning `{ programs: 0, container: null }` on three).
|
|
288
|
+
* Destroy `entity` — content you added under `handle.engine.root` — and release what it held on
|
|
289
|
+
* the GPU: the shader programs its materials linked (PlayCanvas never frees a program by itself:
|
|
290
|
+
* its ProgramLibrary caches every variant for the device's life, and `Material.destroy()` does
|
|
291
|
+
* not destroy them), and, with `asset`, the glTF container it was instantiated from (unloaded
|
|
292
|
+
* and taken out of the registry unless something else in the tile still draws from it). A
|
|
293
|
+
* program another material still uses is kept. Call it for whatever a content swap replaces.
|
|
294
|
+
* Returns the number of programs destroyed and what happened to the container.
|
|
295
|
+
* docs/content-lifecycle-rules.md.
|
|
296
|
+
*/
|
|
297
|
+
releaseEntity(entity: unknown, opts?: { asset?: unknown }): { programs: number; container: 'unloaded' | 'in-use' | null };
|
|
286
298
|
unexclude(el: Element): void;
|
|
287
299
|
/**
|
|
288
300
|
* The core window's `TileHandle.firstWoven`: when it is safe to reveal the canvas. Resolves
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@displayxr/inline3d",
|
|
3
|
-
"version": "1.47.
|
|
3
|
+
"version": "1.47.6",
|
|
4
4
|
"description": "Turn any HTML <canvas> into a 3D window on a spatial display, inside an ordinary web page, for the DisplayXR Browser. Dependency-free; progressive enhancement (falls back to plain 2D everywhere else).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./index.d.ts",
|
|
@@ -74,6 +74,7 @@
|
|
|
74
74
|
"llms.txt",
|
|
75
75
|
"docs/making-3d-look-right.md",
|
|
76
76
|
"docs/woven-canvas-rules.md",
|
|
77
|
+
"docs/content-lifecycle-rules.md",
|
|
77
78
|
"js/inline3d.js",
|
|
78
79
|
"js/inline3d-undock.js",
|
|
79
80
|
"js/inline3d-display-metrics.js",
|
|
@@ -108,6 +109,9 @@
|
|
|
108
109
|
"js/inline3d-model-entry.js",
|
|
109
110
|
"js/inline3d-model-playcanvas.js",
|
|
110
111
|
"js/inline3d-pc-look.js",
|
|
112
|
+
"js/inline3d-pc-auto-release.js",
|
|
113
|
+
"js/inline3d-pc-release.js",
|
|
114
|
+
"js/inline3d-pc-shader-cache.js",
|
|
111
115
|
"js/inline3d-playcanvas-engine.js",
|
|
112
116
|
"js/inline3d-player.js",
|
|
113
117
|
"js/inline3d-camera.js",
|
package/splat.d.ts
CHANGED
|
@@ -1135,6 +1135,18 @@ export interface SplatHandle {
|
|
|
1135
1135
|
* it after this resolves. Resolves the number of programs warmed. docs/playcanvas-adapter.md §warmEntity.
|
|
1136
1136
|
*/
|
|
1137
1137
|
warmEntity(entity: unknown, opts?: { timeoutMs?: number }): Promise<number>;
|
|
1138
|
+
/**
|
|
1139
|
+
* `engine: 'playcanvas'` only (a no-op returning `{ programs: 0, container: null }` on Spark).
|
|
1140
|
+
* Destroy `entity` — content you added under `handle.engine.root` — and release what it held on
|
|
1141
|
+
* the GPU: the shader programs its materials linked (PlayCanvas never frees a program by itself:
|
|
1142
|
+
* its ProgramLibrary caches every variant for the device's life, and `Material.destroy()` does
|
|
1143
|
+
* not destroy them), and, with `asset`, the glTF container it was instantiated from (unloaded
|
|
1144
|
+
* and taken out of the registry unless something else in the tile still draws from it). A
|
|
1145
|
+
* program another material still uses is kept. Call it for whatever a content swap replaces.
|
|
1146
|
+
* Returns the number of programs destroyed and what happened to the container.
|
|
1147
|
+
* docs/content-lifecycle-rules.md.
|
|
1148
|
+
*/
|
|
1149
|
+
releaseEntity(entity: unknown, opts?: { asset?: unknown }): { programs: number; container: 'unloaded' | 'in-use' | null };
|
|
1138
1150
|
setStereo(opts: SplatStereoOptions | null): SplatHandle;
|
|
1139
1151
|
/**
|
|
1140
1152
|
* The `depth` option's comfort factor in force, in (0, 1]: what the display rig's ipdFactor and
|