@carbonenginejs/runtime-resource 0.8.0 → 0.9.1
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/LICENSE +21 -21
- package/NOTICE +31 -29
- package/README.md +67 -548
- package/dist/CjsMotherLode.js +1271 -1271
- package/dist/CjsResMan.js +3005 -3005
- package/dist/CjsResManQueue.js +214 -214
- package/dist/CjsResource.js +566 -566
- package/dist/CjsResourceSource.js +68 -59
- package/dist/CjsResourceSource.js.map +1 -1
- package/dist/_virtual/_rollupPluginBabelHelpers.js +153 -153
- package/dist/format/CjsBlueReader.js +269 -269
- package/dist/format/CjsFormat.js +197 -192
- package/dist/format/CjsFormat.js.map +1 -1
- package/dist/format/CjsReader.js +18 -18
- package/dist/format/CjsResourceProbe.js +277 -277
- package/dist/format/payloadContract.js +173 -173
- package/dist/formats/black/CjsBlackFormat.js +278 -278
- package/dist/formats/black/core/CjsBlackBinaryReader.js +157 -153
- package/dist/formats/black/core/CjsBlackBinaryReader.js.map +1 -1
- package/dist/formats/black/core/CjsBlackPropertyReaders.js +387 -382
- package/dist/formats/black/core/CjsBlackPropertyReaders.js.map +1 -1
- package/dist/formats/black/core/CjsBlackReader.js +639 -639
- package/dist/formats/black/core/CjsBlackSchemaRegistry.js +437 -433
- package/dist/formats/black/core/CjsBlackSchemaRegistry.js.map +1 -1
- package/dist/formats/black/core/black-schema-v1-2026-07-23.json.js +4 -0
- package/dist/formats/black/core/black-schema-v1-2026-07-23.json.js.map +1 -0
- package/dist/formats/black/core/blackConstants.js +7 -7
- package/dist/formats/black/core/blackDefinitions.js +10 -10
- package/dist/formats/black/core/blackDefinitions.js.map +1 -1
- package/dist/formats/black/core/blackEnums.js +6 -6
- package/dist/formats/black/core/blackSchema.js +3 -3
- package/dist/formats/black/core/blackVersion.js +22 -22
- package/dist/formats/black/core/helpers.js +182 -182
- package/dist/formats/black/core/schema.js +4 -4
- package/dist/formats/black/index.js +2 -2
- package/dist/formats/bnk/CjsBnkFormat.js +125 -125
- package/dist/formats/bnk/core/graph.js +140 -140
- package/dist/formats/bnk/core/helpers.js +409 -409
- package/dist/formats/bnk/core/musicNodes.js +493 -489
- package/dist/formats/bnk/core/musicNodes.js.map +1 -1
- package/dist/formats/bnk/core/soundbanksInfo.js +246 -246
- package/dist/formats/bnk/index.js +2 -2
- package/dist/formats/cmf/CjsCmfFormat.js +497 -497
- package/dist/formats/cmf/core/binary.js +123 -118
- package/dist/formats/cmf/core/binary.js.map +1 -1
- package/dist/formats/cmf/core/buffers.js +233 -233
- package/dist/formats/cmf/core/constants.js +47 -47
- package/dist/formats/cmf/core/gr2Anim.js +453 -453
- package/dist/formats/cmf/core/helpers.js +287 -287
- package/dist/formats/cmf/core/pack.js +276 -276
- package/dist/formats/cmf/core/schema.js +364 -364
- package/dist/formats/cmf/core/shared.js +268 -268
- package/dist/formats/cmf/core/writer.js +517 -517
- package/dist/formats/cmf/index.js +2 -2
- package/dist/formats/dds/CjsDdsFormat.js +200 -193
- package/dist/formats/dds/CjsDdsFormat.js.map +1 -1
- package/dist/formats/dds/core/bc6h.js +288 -288
- package/dist/formats/dds/core/bc7.js +256 -251
- package/dist/formats/dds/core/bc7.js.map +1 -1
- package/dist/formats/dds/core/helpers.js +815 -815
- package/dist/formats/dds/index.js +2 -2
- package/dist/formats/fbx/CjsFbxFormat.js +266 -266
- package/dist/formats/fbx/core/helpers.js +3911 -3901
- package/dist/formats/fbx/core/helpers.js.map +1 -1
- package/dist/formats/fbx/index.js +2 -2
- package/dist/formats/flac/CjsFlacFormat.js +93 -87
- package/dist/formats/flac/CjsFlacFormat.js.map +1 -1
- package/dist/formats/flac/core/helpers.js +295 -295
- package/dist/formats/flac/index.js +2 -2
- package/dist/formats/gif/CjsGifFormat.js +92 -87
- package/dist/formats/gif/CjsGifFormat.js.map +1 -1
- package/dist/formats/gif/core/helpers.js +360 -360
- package/dist/formats/gif/index.js +2 -2
- package/dist/formats/gltf/CjsGltfFormat.js +290 -290
- package/dist/formats/gltf/core/helpers.js +292 -292
- package/dist/formats/gltf/core/json.js +79 -79
- package/dist/formats/gltf/core/parser.js +666 -666
- package/dist/formats/gltf/core/targets.js +163 -163
- package/dist/formats/gltf/index.js +2 -2
- package/dist/formats/gr2/CjsGr2Format.js +46 -0
- package/dist/formats/gr2/CjsGr2Format.js.map +1 -0
- package/dist/formats/gr2/core/CjsFormatGr2.js +273 -0
- package/dist/formats/gr2/core/CjsFormatGr2.js.map +1 -0
- package/dist/formats/gr2/core/bitknit2.js +280 -0
- package/dist/formats/gr2/core/bitknit2.js.map +1 -0
- package/dist/formats/gr2/core/curves.js +1047 -0
- package/dist/formats/gr2/core/curves.js.map +1 -0
- package/dist/formats/gr2/core/gsf.js +72 -0
- package/dist/formats/gr2/core/gsf.js.map +1 -0
- package/dist/formats/gr2/core/helpers.js +332 -0
- package/dist/formats/gr2/core/helpers.js.map +1 -0
- package/dist/formats/gr2/core/json.js +622 -0
- package/dist/formats/gr2/core/json.js.map +1 -0
- package/dist/formats/gr2/core/oodle1.js +388 -0
- package/dist/formats/gr2/core/oodle1.js.map +1 -0
- package/dist/formats/gr2/core/reader.js +617 -0
- package/dist/formats/gr2/core/reader.js.map +1 -0
- package/dist/formats/gr2/core/tangents.js +48 -0
- package/dist/formats/gr2/core/tangents.js.map +1 -0
- package/dist/formats/gr2/core/targets.js +351 -0
- package/dist/formats/gr2/core/targets.js.map +1 -0
- package/dist/formats/gr2/index.js +3 -0
- package/dist/formats/gr2/index.js.map +1 -0
- package/dist/formats/index.js +30 -23
- package/dist/formats/index.js.map +1 -1
- package/dist/formats/jpeg/CjsJpegFormat.js +212 -206
- package/dist/formats/jpeg/CjsJpegFormat.js.map +1 -1
- package/dist/formats/jpeg/core/helpers.js +350 -350
- package/dist/formats/jpeg/core/jpeg.js +387 -377
- package/dist/formats/jpeg/core/jpeg.js.map +1 -1
- package/dist/formats/jpeg/index.js +2 -2
- package/dist/formats/mp3/CjsMp3Format.js +197 -192
- package/dist/formats/mp3/CjsMp3Format.js.map +1 -1
- package/dist/formats/mp3/core/helpers.js +338 -338
- package/dist/formats/mp3/index.js +2 -2
- package/dist/formats/mp4/CjsMp4Format.js +197 -191
- package/dist/formats/mp4/CjsMp4Format.js.map +1 -1
- package/dist/formats/mp4/core/helpers.js +449 -449
- package/dist/formats/mp4/index.js +2 -2
- package/dist/formats/obj/CjsObjFormat.js +253 -253
- package/dist/formats/obj/core/helpers.js +573 -573
- package/dist/formats/obj/core/json.js +64 -64
- package/dist/formats/obj/core/parser.js +321 -321
- package/dist/formats/obj/index.js +2 -2
- package/dist/formats/ogg/CjsOggFormat.js +94 -88
- package/dist/formats/ogg/CjsOggFormat.js.map +1 -1
- package/dist/formats/ogg/core/helpers.js +387 -387
- package/dist/formats/ogg/core/imdct.js +178 -178
- package/dist/formats/ogg/core/vorbis.js +1004 -999
- package/dist/formats/ogg/core/vorbis.js.map +1 -1
- package/dist/formats/ogg/index.js +2 -2
- package/dist/formats/png/CjsPngFormat.js +201 -195
- package/dist/formats/png/CjsPngFormat.js.map +1 -1
- package/dist/formats/png/core/helpers.js +583 -583
- package/dist/formats/png/index.js +2 -2
- package/dist/formats/red/CjsRedFormat.js +261 -261
- package/dist/formats/red/core/CjsRedReader.js +194 -194
- package/dist/formats/red/core/blackDefinitions.js +3 -3
- package/dist/formats/red/core/helpers.js +139 -139
- package/dist/formats/red/core/redGraph.js +68 -68
- package/dist/formats/red/core/schema.js +4 -4
- package/dist/formats/red/index.js +2 -2
- package/dist/formats/stl/CjsStlFormat.js +365 -365
- package/dist/formats/stl/core/helpers.js +261 -261
- package/dist/formats/stl/core/json.js +51 -51
- package/dist/formats/stl/core/stl.js +634 -629
- package/dist/formats/stl/core/stl.js.map +1 -1
- package/dist/formats/stl/core/targets.js +163 -163
- package/dist/formats/stl/index.js +2 -2
- package/dist/formats/tga/CjsTgaFormat.js +197 -192
- package/dist/formats/tga/CjsTgaFormat.js.map +1 -1
- package/dist/formats/tga/core/helpers.js +446 -446
- package/dist/formats/tga/index.js +2 -2
- package/dist/formats/wav/CjsWavFormat.js +198 -192
- package/dist/formats/wav/CjsWavFormat.js.map +1 -1
- package/dist/formats/wav/core/helpers.js +328 -328
- package/dist/formats/wav/index.js +2 -2
- package/dist/formats/webm/CjsWebmFormat.js +197 -191
- package/dist/formats/webm/CjsWebmFormat.js.map +1 -1
- package/dist/formats/webm/core/helpers.js +537 -537
- package/dist/formats/webm/index.js +2 -2
- package/dist/formats/webp/CjsWebpFormat.js +91 -86
- package/dist/formats/webp/CjsWebpFormat.js.map +1 -1
- package/dist/formats/webp/core/helpers.js +214 -214
- package/dist/formats/webp/index.js +2 -2
- package/dist/formats/wem/CjsWemFormat.js +242 -241
- package/dist/formats/wem/CjsWemFormat.js.map +1 -1
- package/dist/formats/wem/core/bitStream.js +259 -259
- package/dist/formats/wem/core/codebookLibrary.js +164 -164
- package/dist/formats/wem/core/helpers.js +417 -417
- package/dist/formats/wem/core/packedCodebooksAotuv603.js +30 -30
- package/dist/formats/wem/core/ptadpcm.js +77 -77
- package/dist/formats/wem/core/resolve.js +121 -121
- package/dist/formats/wem/core/wemToOgg.js +485 -485
- package/dist/formats/wem/index.js +2 -2
- package/dist/formats/yaml/CjsYamlFormat.js +89 -83
- package/dist/formats/yaml/CjsYamlFormat.js.map +1 -1
- package/dist/formats/yaml/core/CjsYamlReader.js +311 -305
- package/dist/formats/yaml/core/CjsYamlReader.js.map +1 -1
- package/dist/formats/yaml/core/helpers.js +160 -160
- package/dist/formats/yaml/index.js +2 -2
- package/dist/index.js +49 -48
- package/dist/index.js.map +1 -1
- package/dist/resourcePath.js +18 -18
- package/dist/resourceStates.js +9 -9
- package/dist/resources/AudioGeometryResData.js +47 -47
- package/dist/resources/GStateBindingCallbackData.js +31 -31
- package/dist/resources/MeshDecalData.js +37 -37
- package/dist/resources/MeshDecalLodData.js +34 -34
- package/dist/resources/Tr2EffectRes.js +71 -71
- package/dist/resources/Tr2GrannyIntersectionResult.js +60 -60
- package/dist/resources/Tr2GrannyStateRes.js +44 -44
- package/dist/resources/Tr2ImageRes.js +114 -114
- package/dist/resources/Tr2LightProfileRes.js +40 -40
- package/dist/resources/Tr2MaterialArea.js +34 -34
- package/dist/resources/Tr2MaterialMesh.js +31 -31
- package/dist/resources/Tr2MaterialRes.js +34 -34
- package/dist/resources/Tr2ShaderPermutation.js +43 -43
- package/dist/resources/Tr2TextureLodManager.js +80 -80
- package/dist/resources/Tr2TextureLodUpdateRequest.js +37 -37
- package/dist/resources/Tr2TexturePackChannel.js +37 -37
- package/dist/resources/Tr2TexturePipeline.js +52 -52
- package/dist/resources/Tr2TexturePipelineParams.js +34 -34
- package/dist/resources/Tr2TexturePipelineStepCompress.js +40 -40
- package/dist/resources/Tr2TexturePipelineStepGenerateMips.js +22 -0
- package/dist/resources/Tr2TexturePipelineStepGenerateMips.js.map +1 -0
- package/dist/resources/Tr2TexturePipelineStepLimitSize.js +34 -34
- package/dist/resources/Tr2TexturePipelineStepLoad.js +31 -31
- package/dist/resources/Tr2TexturePipelineStepPack.js +43 -43
- package/dist/resources/TriGeometryRes.js +239 -239
- package/dist/resources/TriGeometryResAreaData.js +59 -59
- package/dist/resources/TriGeometryResJointData.js +38 -38
- package/dist/resources/TriGeometryResLodData.js +88 -88
- package/dist/resources/TriGeometryResMeshData.js +63 -63
- package/dist/resources/TriGeometryResSkeletonData.js +34 -34
- package/dist/resources/TriGrannyRes.js +43 -43
- package/dist/resources/TriJointBinding.js +38 -38
- package/dist/resources/TriMorphTargetGeometryConstants.js +46 -46
- package/dist/resources/TriRtGeometryConstants.js +88 -88
- package/dist/resources/TriTextureRes.js +304 -304
- package/dist/resources/enums.js +18 -18
- package/dist/resources/resourceBoundary.js +47 -47
- package/dist/resources/texturePipelineBehavior.js +308 -308
- package/dist/texture/CjsTextureArrayRes.js +406 -406
- package/dist/texture/CjsTextureParameterProxy.js +133 -133
- package/docs/README.md +73 -0
- package/docs/architecture.md +86 -0
- package/docs/concepts/resource-lifecycle.md +217 -0
- package/docs/formats/README.md +105 -0
- package/docs/formats/gr2.md +161 -0
- package/{FORMAT-PROVENANCE.md → docs/formats/provenance.md} +173 -155
- package/docs/formats/stl.md +37 -0
- package/docs/formats/wwise.md +44 -0
- package/docs/reference/classes/README.md +33 -0
- package/docs/reference/classes/core.md +106 -0
- package/docs/reference/classes/dropped.md +46 -0
- package/docs/reference/classes/formats.md +522 -0
- package/docs/reference/classes/resources.md +346 -0
- package/docs/reference/classes/texture.md +26 -0
- package/docs/reference/events.md +92 -0
- package/docs/reference/motherlode-cache.md +244 -0
- package/docs/reference/queues.md +102 -0
- package/docs/reference/reload.md +107 -0
- package/docs/reference/texture-arrays.md +113 -0
- package/docs/reference/texture-pipeline.md +53 -0
- package/docs/roadmap.md +104 -0
- package/format-notices/black/LICENSE +21 -21
- package/format-notices/black/NOTICE +47 -47
- package/format-notices/bnk/LICENSE +21 -21
- package/format-notices/bnk/NOTICE +20 -20
- package/format-notices/cmf/LICENSE +21 -21
- package/format-notices/cmf/NOTICE +36 -36
- package/format-notices/dds/LICENSE +21 -21
- package/format-notices/dds/NOTICE +14 -14
- package/format-notices/fbx/LICENSE +21 -21
- package/format-notices/fbx/NOTICE +14 -14
- package/format-notices/flac/LICENSE +21 -21
- package/format-notices/flac/NOTICE +14 -14
- package/format-notices/gif/LICENSE +21 -21
- package/format-notices/gif/NOTICE +14 -14
- package/format-notices/gltf/LICENSE +21 -21
- package/format-notices/gltf/NOTICE +27 -27
- package/format-notices/gr2/LICENSE +21 -0
- package/format-notices/gr2/NOTICE +60 -0
- package/format-notices/gr2/THIRD-PARTY-NOTICES.md +93 -0
- package/format-notices/jpeg/LICENSE +21 -21
- package/format-notices/jpeg/NOTICE +14 -14
- package/format-notices/mp3/LICENSE +21 -21
- package/format-notices/mp3/NOTICE +14 -14
- package/format-notices/mp4/LICENSE +21 -21
- package/format-notices/mp4/NOTICE +14 -14
- package/format-notices/obj/LICENSE +21 -21
- package/format-notices/obj/NOTICE +26 -26
- package/format-notices/ogg/LICENSE +21 -21
- package/format-notices/ogg/NOTICE +28 -28
- package/format-notices/png/LICENSE +21 -21
- package/format-notices/png/NOTICE +14 -14
- package/format-notices/red/LICENSE +21 -21
- package/format-notices/red/NOTICE +31 -31
- package/format-notices/stl/LICENSE +21 -21
- package/format-notices/stl/NOTICE +21 -21
- package/format-notices/tga/LICENSE +21 -21
- package/format-notices/tga/NOTICE +14 -14
- package/format-notices/wav/LICENSE +21 -21
- package/format-notices/wav/NOTICE +14 -14
- package/format-notices/webm/LICENSE +21 -21
- package/format-notices/webm/NOTICE +14 -14
- package/format-notices/webp/LICENSE +21 -21
- package/format-notices/webp/NOTICE +14 -14
- package/format-notices/wem/LICENSE +57 -57
- package/format-notices/wem/NOTICE +33 -33
- package/format-notices/yaml/LICENSE +21 -21
- package/format-notices/yaml/NOTICE +44 -44
- package/package.json +52 -51
- package/dist/formats/black/core/black-schema-v1-2026-07-11.json.js +0 -4
- package/dist/formats/black/core/black-schema-v1-2026-07-11.json.js.map +0 -1
- package/resource-lifecycle.md +0 -679
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# MotherLode identity, cache, and retention
|
|
2
|
+
|
|
3
|
+
Status: Evolving
|
|
4
|
+
Scope: `@carbonenginejs/runtime-resource`
|
|
5
|
+
Audience: Users and integrators
|
|
6
|
+
Summary: Defines canonical resource identity, ownership and replacement, the recorded-byte cache, payload retention, read-cache provenance, and purge contracts.
|
|
7
|
+
|
|
8
|
+
## Canonical identity
|
|
9
|
+
|
|
10
|
+
Canonical resource identity is the normalized source path plus its promised
|
|
11
|
+
output tag. `variant` is the explicit tag; otherwise `emit`, `requirement`, or
|
|
12
|
+
`payload` supplies it. Human-readable identities are written as
|
|
13
|
+
`res:/ship.gr2@cmf`, although MotherLode uses an internal delimiter. Reader,
|
|
14
|
+
constructor, and format-option implementations never enter the key. CjsLibrary
|
|
15
|
+
chooses the promised output and ResMan executes the current setup-time format
|
|
16
|
+
registration for it.
|
|
17
|
+
|
|
18
|
+
Output selection is case-insensitive for identity and matching, but format
|
|
19
|
+
readers receive the canonical declared spelling (for example `cmfJson`). A
|
|
20
|
+
legacy direct object loader exposes only its unforced default; named output
|
|
21
|
+
variants belong on a format class. Unsupported `@output` requests fail before
|
|
22
|
+
cache lookup, so a resident handle cannot bypass the declaration.
|
|
23
|
+
|
|
24
|
+
The selected constructor, reader, and format defaults/options are setup-time
|
|
25
|
+
execution details, not MotherLode identity. A changed registration does not
|
|
26
|
+
create a hidden second resource; reset the affected identity
|
|
27
|
+
(`Delete`/`Clear`) or create a new manager. A changed output contract must use
|
|
28
|
+
a new tag such as `@cmf2`.
|
|
29
|
+
|
|
30
|
+
## Ownership and replacement
|
|
31
|
+
|
|
32
|
+
`CjsResMan` resolves each normalized path and promised output to one canonical
|
|
33
|
+
MotherLode key. `Insert(key, resource, options)` reports `{ inserted,
|
|
34
|
+
replaced, displaced }`; replacement, deletion, clearing, and shutdown destroy
|
|
35
|
+
attached adapter allocations and release the complete CPU payload by default.
|
|
36
|
+
Callers that deliberately retain ownership may pass `{ cleanup: false }` and
|
|
37
|
+
keep the returned displaced resource. If replacement cleanup fails, insertion
|
|
38
|
+
throws a contextual error and leaves the existing owner registered. These
|
|
39
|
+
ordinary ownership removals preserve the handle's last resource state;
|
|
40
|
+
`PURGED` is reserved for successful policy eviction through inactivity or
|
|
41
|
+
byte pressure.
|
|
42
|
+
|
|
43
|
+
`Startup()` and `Shutdown()` are idempotent. `HasKey`, `Lookup`, `Delete`,
|
|
44
|
+
`GetKeys`, `GetValues`, `GetSize`, `SetCacheSize`, `GetCacheSize`, `GetStats`,
|
|
45
|
+
`TrimCache`, `ReplaceExpected`, `Clear`, and `ClearCached` provide the
|
|
46
|
+
Carbon-shaped cache vocabulary plus the exact-owner compare-and-swap required
|
|
47
|
+
by staged JavaScript reload. The old `Has`, `GetCount`, and `DeleteAll` names
|
|
48
|
+
remain temporary compatibility aliases.
|
|
49
|
+
|
|
50
|
+
## Recorded-byte cache
|
|
51
|
+
|
|
52
|
+
Carbon can infer when only its cache retains a resource through
|
|
53
|
+
weak-reference and refcount transitions. JavaScript cannot reproduce that
|
|
54
|
+
ownership test reliably, so `CjsMotherLode` budgets only entries that a caller
|
|
55
|
+
explicitly classifies with `{ cached: true, bytes }`; JavaScript reachability
|
|
56
|
+
is never inferred. The byte value is an exact caller-supplied safe-integer
|
|
57
|
+
eviction weight, not a heuristic walk of the resource graph; runtime-resource
|
|
58
|
+
does not walk arbitrary cyclic/shared object graphs or invoke payload getters
|
|
59
|
+
to guess size.
|
|
60
|
+
|
|
61
|
+
Explicit cached entries receive a monotonic admission sequence. With default
|
|
62
|
+
cleanup, `TrimCache(options)` destroys adapters, releases payloads, detaches
|
|
63
|
+
lifecycle callbacks, marks compatible handles `PURGED`, and removes
|
|
64
|
+
positive-byte cached identities in oldest-admission order until
|
|
65
|
+
`cacheBytes <= cacheSize`. Live, locked, `cacheable: false`, and zero-byte
|
|
66
|
+
entries do not create pressure. `KeepAlive()` and `Lock()` promote a cached
|
|
67
|
+
record to live; `Unlock()` does not silently re-admit it.
|
|
68
|
+
|
|
69
|
+
`SetCacheSize(bytes, options)` installs and immediately enforces the new
|
|
70
|
+
budget. `CjsResMan.Update()` and `Tick()` retry cache housekeeping after
|
|
71
|
+
pumping queues; `{ cache: false }` skips it for one update without changing
|
|
72
|
+
policy. Cleanup is transactional per identity: a failed candidate remains
|
|
73
|
+
canonical, later candidates are still attempted, and the aggregate
|
|
74
|
+
`CJS_MOTHERLODE_CACHE_TRIM_FAILED` error carries successful evictions and any
|
|
75
|
+
remaining over-budget state. Trimming never reads, prepares, or reloads data.
|
|
76
|
+
|
|
77
|
+
## Payload retention
|
|
78
|
+
|
|
79
|
+
Reader and converter outputs are plain transient payload objects, not resource
|
|
80
|
+
classes or DTO models. A payload may contain more decoded data than a
|
|
81
|
+
particular resource or engine adapter needs. Each concrete resource validates
|
|
82
|
+
the fields it requires before publishing the payload and retains the scalars
|
|
83
|
+
and references it needs. An adapter may retain additional references in
|
|
84
|
+
adapter-owned state. Referencing payload-owned typed arrays is valid and
|
|
85
|
+
preferable to copying them merely to change ownership.
|
|
86
|
+
|
|
87
|
+
The lifecycle treats resource residency and payload residency independently:
|
|
88
|
+
|
|
89
|
+
```text
|
|
90
|
+
resource.KeepAlive()
|
|
91
|
+
-> renew resource/cache residency
|
|
92
|
+
|
|
93
|
+
resource.KeepPayloadAlive()
|
|
94
|
+
-> renew the attached payload lease
|
|
95
|
+
|
|
96
|
+
resource.ReleasePayload()
|
|
97
|
+
-> explicitly release the full payload reference
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
`CjsResMan` binds resource-facing `KeepAlive()`, `KeepPayloadAlive()`,
|
|
101
|
+
`Lock()`, and `Unlock()` to the resource's canonical MotherLode key.
|
|
102
|
+
`SetPayload()` renews both identity and payload activity when it publishes a
|
|
103
|
+
non-null payload. `GetPayload()`, `HasPayload()`, `IsGood()`, and other
|
|
104
|
+
queries are pure; reading the payload does not implicitly renew its lease.
|
|
105
|
+
Detached and purged handles retain deterministic no-op liveness methods rather
|
|
106
|
+
than silently starting work.
|
|
107
|
+
|
|
108
|
+
A released CPU payload retains only the small request needed to reconstruct
|
|
109
|
+
that same path/output from its source and `sourceRevision`. The retained
|
|
110
|
+
promised-output fields and source provenance win over later
|
|
111
|
+
`Ready()`/`GetObject()` overrides, while cache/reload policy remains per-call.
|
|
112
|
+
Payload leases protect active consumers; an engine may release its own backend
|
|
113
|
+
adapter without destroying shared CPU data.
|
|
114
|
+
|
|
115
|
+
Both semantic and generic/base resource results use the payload slot: the
|
|
116
|
+
manager stores the complete result through `SetPayload()` and mirrors it on
|
|
117
|
+
the compatibility `object` property for base resources. Payload release clears
|
|
118
|
+
that alias only while it still identifies the released value. Semantic
|
|
119
|
+
resources continue to expose `object === resource` while holding their
|
|
120
|
+
validated plain payload privately.
|
|
121
|
+
|
|
122
|
+
Object-operation promises are retained only while in flight. Concurrent
|
|
123
|
+
`GetObject()`/`Ready()` calls share one operation; a resident result is
|
|
124
|
+
returned without source work and renews the explicit payload lease.
|
|
125
|
+
Settlement removes the operation record so its result graph can be reclaimed
|
|
126
|
+
and a failure can be retried. After payload release, only a new explicit
|
|
127
|
+
object/readiness call reconstructs it; queries, lease calls, and purge sweeps
|
|
128
|
+
do not.
|
|
129
|
+
|
|
130
|
+
The processor preparing a resource decides when the full payload can be
|
|
131
|
+
released:
|
|
132
|
+
|
|
133
|
+
```text
|
|
134
|
+
format reader -> plain payload -> resource validation + adapter prepare
|
|
135
|
+
|
|
|
136
|
+
+-> resource retains required values/references
|
|
137
|
+
+-> adapter retains adapter-specific state/references
|
|
138
|
+
+-> release payload after successful preparation
|
|
139
|
+
`-> or renew its lease for deferred/further work
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
A time- or frame-based lease is a fallback against abandoned payloads. An
|
|
143
|
+
owner performing deferred work can renew the lease. If an expired payload is
|
|
144
|
+
required again, the caller must explicitly request reconstruction; lease
|
|
145
|
+
renewal and purging never fetch or reload source data. Dynamic or
|
|
146
|
+
non-reloadable resources must remain locked, retain the required payload, or
|
|
147
|
+
be able to recreate it.
|
|
148
|
+
|
|
149
|
+
Payload references are shared read-only by default. Preparing WebGL and
|
|
150
|
+
WebGPU adapters side by side should normally pass the same payload to both
|
|
151
|
+
consumers and retain it until both have finished. Copying is an explicit
|
|
152
|
+
consumer operation, justified when a consumer must mutate data, transfer and
|
|
153
|
+
detach an `ArrayBuffer`, or retain an independently writable snapshot. The
|
|
154
|
+
consumer should copy only the fields it requires; runtime-resource does not
|
|
155
|
+
automatically deep-clone payload or typed-array bundles.
|
|
156
|
+
|
|
157
|
+
## Read-cache provenance
|
|
158
|
+
|
|
159
|
+
Source and parsed-format caches use explicit provenance, separate from
|
|
160
|
+
path/output resource identity. `sourceRevision` is an opaque caller/source-
|
|
161
|
+
supplied string or finite number identifying source content for one source
|
|
162
|
+
object and normalized path. It scopes read caches only; it does not alter
|
|
163
|
+
MotherLode resource identity, and changing it does not replace a resident
|
|
164
|
+
payload without `reload: true`. Source and format records do not share across
|
|
165
|
+
revisions.
|
|
166
|
+
|
|
167
|
+
`cacheSource` and `cacheFormat` are tri-state per-call policies:
|
|
168
|
+
|
|
169
|
+
- omitted: share in-flight or explicitly retained work, then drop a newly
|
|
170
|
+
completed record;
|
|
171
|
+
- `true`: share and retain success; a joining caller upgrades the record;
|
|
172
|
+
- `false`: bypass sharing and retention.
|
|
173
|
+
|
|
174
|
+
Failures are never retained. Format records are additionally isolated by
|
|
175
|
+
selected source object, frozen registration descriptor, revision, and
|
|
176
|
+
effective format options, so another source or a re-registered default cannot
|
|
177
|
+
reuse a stale parse. Re-registering a format with new defaults therefore
|
|
178
|
+
cannot reuse an old descriptor's parse. Registered defaults are copied into
|
|
179
|
+
deeply frozen plain-object/array snapshots. Material format options that
|
|
180
|
+
cannot be represented safely (for example class instances with hidden mutable
|
|
181
|
+
state) bypass format-cache sharing instead of risking a false match;
|
|
182
|
+
functions and byte views use cache-local identity plus visible byte content
|
|
183
|
+
where applicable.
|
|
184
|
+
|
|
185
|
+
A resource loader retains the effective selected source and `sourceRevision`
|
|
186
|
+
for reconstruction, including the manager default selected at creation, but
|
|
187
|
+
not cache flags or one-shot reload.
|
|
188
|
+
|
|
189
|
+
## Explicit and automatic purging
|
|
190
|
+
|
|
191
|
+
`PurgeInactive(options)` performs an explicit deterministic sweep using
|
|
192
|
+
independent identity and payload frame/time limits. Locks skip both forms of
|
|
193
|
+
eviction. Identity expiry destroys adapter resources, releases the payload,
|
|
194
|
+
detaches lifecycle callbacks, marks compatible handles `PURGED`, and removes
|
|
195
|
+
the canonical key; payload expiry calls `ReleasePayload()` while retaining
|
|
196
|
+
identity and adapter allocations. Candidate failures are aggregated after the
|
|
197
|
+
sweep has continued over other entries. A sweep never fetches, prepares, or
|
|
198
|
+
reloads a resource.
|
|
199
|
+
|
|
200
|
+
Automatic scheduling is available only when a caller supplies
|
|
201
|
+
`autoPurgePolicy` to the constructor/`Register()` or calls
|
|
202
|
+
`SetAutoPurgePolicy()`. It is disabled by default and deliberately accepts
|
|
203
|
+
only millisecond limits: MotherLode activity frames count explicit
|
|
204
|
+
observations and are not renderer frames. A policy must set at least one of
|
|
205
|
+
`maxIdleMilliseconds` or `payloadMaxIdleMilliseconds`;
|
|
206
|
+
`intervalMilliseconds` defaults to 1000. The first
|
|
207
|
+
`PumpAutoPurge()`/`Update()` after configuration sweeps immediately, then the
|
|
208
|
+
interval sets the minimum cadence. `Update({ purge: false })` suppresses a
|
|
209
|
+
sweep for one update without changing cadence. A regressing clock rebases and
|
|
210
|
+
skips one pump; custom deterministic clocks should be shared with MotherLode.
|
|
211
|
+
Recorded-byte cache trimming is separate from this opt-in inactivity policy
|
|
212
|
+
and runs on ordinary updates unless `{ cache: false }` is supplied.
|
|
213
|
+
|
|
214
|
+
```js
|
|
215
|
+
const resMan = new CjsResMan({
|
|
216
|
+
source,
|
|
217
|
+
autoPurgePolicy: {
|
|
218
|
+
intervalMilliseconds: 1000,
|
|
219
|
+
maxIdleMilliseconds: 60_000,
|
|
220
|
+
payloadMaxIdleMilliseconds: 10_000
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
resMan.Update();
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Both queued `QueueResourceObject()` work and direct `LoadResourceObject()`
|
|
228
|
+
work hold one manager-owned lock from request/loading publication through
|
|
229
|
+
success or failure. The lock is balanced independently of caller locks, so
|
|
230
|
+
automatic or manual sweeps cannot detach a handle while its read/prepare
|
|
231
|
+
operation is still active. Lock release is conditional on the same captured
|
|
232
|
+
ownership generation, so stale work cannot decrement a newly rebound handle's
|
|
233
|
+
lock. Scheduling and active-work protection do not fetch or reload data.
|
|
234
|
+
|
|
235
|
+
Cache trimming and automatic inactivity sweeps retain the strict no-reload
|
|
236
|
+
rule. Application retention defaults, automatic resource/payload byte
|
|
237
|
+
estimation, separate CPU/adapter budgets, and purged-resource/device-loss
|
|
238
|
+
recovery policy are future work; see the [roadmap](../roadmap.md).
|
|
239
|
+
|
|
240
|
+
## Related documentation
|
|
241
|
+
|
|
242
|
+
- [Resource lifecycle concepts](../concepts/resource-lifecycle.md)
|
|
243
|
+
- [Candidate-first atomic reload](../reference/reload.md)
|
|
244
|
+
- [Queues and the Wait fence](../reference/queues.md)
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Queues, publication, and the Wait fence
|
|
2
|
+
|
|
3
|
+
Status: Evolving
|
|
4
|
+
Scope: `@carbonenginejs/runtime-resource`
|
|
5
|
+
Audience: Users and integrators
|
|
6
|
+
Summary: Defines the two manager-owned queues, publication budgeting, registration, and the `Wait()` fence semantics.
|
|
7
|
+
|
|
8
|
+
## Queued CPU load and publication
|
|
9
|
+
|
|
10
|
+
`GetObject()`, `LoadObject()`, and resource `Ready()` use two manager-owned
|
|
11
|
+
queues:
|
|
12
|
+
|
|
13
|
+
```text
|
|
14
|
+
BACKGROUND: deduplicated source load, limited by maxConcurrentLoads
|
|
15
|
+
MAIN: reader/format conversion -> resource publication
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The main reader/format operation and publication are separate queue items.
|
|
19
|
+
`maxPrepareTime` is a per-pump budget in seconds limiting synchronous
|
|
20
|
+
main-queue work started in one pump, and `maxPrepareItemsPerTick` can add an
|
|
21
|
+
item-count limit. Promise-returning format work remains in flight without
|
|
22
|
+
blocking the JavaScript event loop. The default scheduler keeps promise-based
|
|
23
|
+
calls working; a `CjsLibrary` or direct caller can provide its frame
|
|
24
|
+
scheduler:
|
|
25
|
+
|
|
26
|
+
```js
|
|
27
|
+
const resMan = new CjsResMan({
|
|
28
|
+
source,
|
|
29
|
+
maxConcurrentLoads: 8,
|
|
30
|
+
maxPrepareTime: 0.005,
|
|
31
|
+
queueScheduler: callback => requestAnimationFrame(callback)
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
await resMan.FetchResource("res:/model/ship.gr2", {
|
|
35
|
+
requirement: "geometry",
|
|
36
|
+
emit: "cmf"
|
|
37
|
+
});
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The selected format class owns conversion to the promised CPU output.
|
|
41
|
+
`CjsResMan` does not inspect WebGL, WebGPU, texture, geometry, or codec
|
|
42
|
+
support, and it does not run backend realization. An engine consumes the
|
|
43
|
+
published CPU resource afterward through its own explicit operation.
|
|
44
|
+
|
|
45
|
+
## Queue controls
|
|
46
|
+
|
|
47
|
+
Blue-compatible queue controls are exposed directly on `CjsResMan`:
|
|
48
|
+
`AddToQueue`, `CancelFromQueue`, `GetNextIdForQueue`, `PumpMainThreadQueue`,
|
|
49
|
+
`PauseQueue`, `ResumeQueue`, `GetPendingLoads`, and `GetPendingPrepares`.
|
|
50
|
+
`Update()`/`Tick()` pump work.
|
|
51
|
+
|
|
52
|
+
## The Wait fence
|
|
53
|
+
|
|
54
|
+
`Wait()` synchronously captures queued resource-operation roots and low-level
|
|
55
|
+
queue tasks that already exist when it is called. Captured resource roots
|
|
56
|
+
include publication work enqueued after an asynchronous read; unrelated later
|
|
57
|
+
roots/tasks do not postpone the fence. Failure and queued cancellation count
|
|
58
|
+
as settlement, remain observable through their original operation promises,
|
|
59
|
+
and cross the fence without making `Wait()` reject.
|
|
60
|
+
|
|
61
|
+
By default `Wait()` pumps the two queues directly within their ordinary
|
|
62
|
+
budgets and never runs automatic purge housekeeping. It preserves pause
|
|
63
|
+
state; `{ pump: false }` leaves all progress to an external driver. A
|
|
64
|
+
standalone canonical `PrepareResourceObjectQueued()` call is a queued root.
|
|
65
|
+
Direct `LoadResourceObject()`, direct `PrepareResourceObject()`, standalone
|
|
66
|
+
`ReadResource()`, and standalone `ReadFormatOnce()` calls bypass both queues
|
|
67
|
+
and are outside this fence unless they own a captured queue task, although
|
|
68
|
+
direct resource mutations are still tracked for safe MotherLode replacement.
|
|
69
|
+
`WaitUrgent()` remains deferred until the queue has real per-item priority
|
|
70
|
+
and urgent-membership semantics.
|
|
71
|
+
|
|
72
|
+
## Registration
|
|
73
|
+
|
|
74
|
+
Format classes own input extensions. Resource classes are registered by a
|
|
75
|
+
semantic requirement, never by file extension:
|
|
76
|
+
|
|
77
|
+
```js
|
|
78
|
+
const resMan = new CjsResMan().Register({
|
|
79
|
+
source,
|
|
80
|
+
formats: [ CjsDdsFormat, CjsPngFormat ],
|
|
81
|
+
resourceTypes: [ TriTextureRes, Tr2ImageRes ]
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const texture = resMan.GetResource("res:/image/ship.png", {
|
|
85
|
+
requirement: "texture",
|
|
86
|
+
emit: "image"
|
|
87
|
+
});
|
|
88
|
+
const image = resMan.GetResource("res:/image/ship.png", {
|
|
89
|
+
requirement: "image",
|
|
90
|
+
emit: "image"
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Those are distinct resource identities but share the normalized source-byte
|
|
95
|
+
operation. The manager does not expose an extension-to-resource compatibility
|
|
96
|
+
registry.
|
|
97
|
+
|
|
98
|
+
## Related documentation
|
|
99
|
+
|
|
100
|
+
- [Resource lifecycle concepts](../concepts/resource-lifecycle.md)
|
|
101
|
+
- [MotherLode identity, cache, and retention](motherlode-cache.md)
|
|
102
|
+
- [Format subpaths](../formats/README.md)
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Candidate-first atomic reload
|
|
2
|
+
|
|
3
|
+
Status: Evolving
|
|
4
|
+
Scope: `@carbonenginejs/runtime-resource`
|
|
5
|
+
Audience: Users and integrators
|
|
6
|
+
Summary: Defines the reload contract that keeps the last published good handle canonical until a distinct candidate has fully succeeded.
|
|
7
|
+
|
|
8
|
+
## Contract
|
|
9
|
+
|
|
10
|
+
Reload is candidate-first. When an owner already exists,
|
|
11
|
+
`GetResource(path, { reload: true })` constructs and initializes a distinct
|
|
12
|
+
off-registry candidate without changing ordinary lookup. Calling `Ready()` on
|
|
13
|
+
that candidate, using `GetObject()` / `FetchResource()` with `reload: true`,
|
|
14
|
+
or calling the explicit `ReloadObject()` / `ReloadResource()` helpers starts
|
|
15
|
+
one shared candidate operation that runs the same queued contract:
|
|
16
|
+
|
|
17
|
+
1. purge-lock the exact former owner and invalidate reusable reads once;
|
|
18
|
+
2. read, convert through the selected format, and publish payload state only
|
|
19
|
+
on the detached candidate;
|
|
20
|
+
3. require the newest per-key reload token and exact former ownership;
|
|
21
|
+
4. compare-and-swap the fully loaded CPU candidate into MotherLode;
|
|
22
|
+
5. invalidate and clean the displaced handle after the lookup switch.
|
|
23
|
+
|
|
24
|
+
The manager captures the exact MotherLode, key, former handle, former
|
|
25
|
+
ownership generation, and a newest-request token. Reader, prepare, and
|
|
26
|
+
publication stages mutate only the detached candidate and validate candidate
|
|
27
|
+
authority before and after asynchronous boundaries. A fully loaded CPU
|
|
28
|
+
candidate commits through `CjsMotherLode.ReplaceExpected()` only if the exact
|
|
29
|
+
former owner and newest token still match. The final authority callback and
|
|
30
|
+
exact-record check run immediately before the synchronous map switch, with no
|
|
31
|
+
user cleanup or `await` between the comparison and publication.
|
|
32
|
+
|
|
33
|
+
After the switch, the displaced ownership generation is invalidated, the
|
|
34
|
+
candidate receives ordinary lifecycle/reconstruction callbacks, and the former
|
|
35
|
+
handle is cleaned exactly once. Existing JavaScript references are not
|
|
36
|
+
retargeted; they keep the displaced handle, while fresh lookup sees the
|
|
37
|
+
committed candidate.
|
|
38
|
+
|
|
39
|
+
## Failure behavior
|
|
40
|
+
|
|
41
|
+
Source, format, or publication failure leaves the former handle, state,
|
|
42
|
+
payload, and adapters canonical; the failed candidate's attached payload and
|
|
43
|
+
adapters are cleaned and its original error is retained. An otherwise-
|
|
44
|
+
successful candidate that was superseded, deleted, cleared, or replaced
|
|
45
|
+
rejects with `CJS_RESMAN_STALE_RELOAD_CANDIDATE` and cannot resurrect the
|
|
46
|
+
key. Constructors that return the former singleton are rejected before
|
|
47
|
+
`Initialize()` can mutate it because staging requires a distinct handle.
|
|
48
|
+
|
|
49
|
+
Failed freshness attempts still invalidate reusable source/format records
|
|
50
|
+
when their work begins; the already-published canonical payload is not
|
|
51
|
+
dependent on those records.
|
|
52
|
+
|
|
53
|
+
Cleanup errors have explicit sides. Candidate cleanup failure aggregates with
|
|
54
|
+
the original preparation/stale error as
|
|
55
|
+
`CJS_RESMAN_RELOAD_CANDIDATE_CLEANUP_FAILED` while the former owner remains
|
|
56
|
+
canonical. A displaced-owner cleanup failure occurs after publication and
|
|
57
|
+
rejects as `CJS_MOTHERLODE_REPLACE_CLEANUP_FAILED`, whose result explicitly
|
|
58
|
+
reports `committed: true`; the already-good candidate remains canonical.
|
|
59
|
+
|
|
60
|
+
## Read-cache interaction
|
|
61
|
+
|
|
62
|
+
`reload: true` synchronously detaches every queued/source/format read record
|
|
63
|
+
for the selected source/path before fresh work starts. Existing consumers
|
|
64
|
+
keep their detached promises; reload does not abort them. Fresh success
|
|
65
|
+
repopulates only caches explicitly requested with `cacheSource: true` or
|
|
66
|
+
`cacheFormat: true`. `InvalidateReadCache(path, { source, sourceRevision })`
|
|
67
|
+
provides the same no-abort invalidation explicitly; omitting
|
|
68
|
+
`sourceRevision` removes all revisions for that source/path. `Delete()`
|
|
69
|
+
remains canonical resource-identity-only, while `Clear()` resets all read
|
|
70
|
+
ledgers. A failed reload preserves the former payload but does not restore
|
|
71
|
+
reusable read-cache entries detached by its explicit freshness request.
|
|
72
|
+
|
|
73
|
+
## Publication authority and staleness
|
|
74
|
+
|
|
75
|
+
Every queued, direct, standalone, and candidate resource preparation captures
|
|
76
|
+
an immutable publication authority: the exact MotherLode, canonical key,
|
|
77
|
+
resource handle, and a manager-local ownership generation. The manager
|
|
78
|
+
validates that authority before and after state changes, asynchronous
|
|
79
|
+
reader/format work, and publication. Delete, Clear, successful reload commit,
|
|
80
|
+
or exact-handle reinsertion therefore makes older work reject with
|
|
81
|
+
`CJS_RESMAN_STALE_RESOURCE_OPERATION` before it can enter another state or
|
|
82
|
+
publish. If stale work independently rejects, its original source/format
|
|
83
|
+
error is preserved and `SetError()` is suppressed on the detached handle.
|
|
84
|
+
|
|
85
|
+
Candidate work is a normal `Wait()` root and blocks synchronous MotherLode
|
|
86
|
+
replacement while active. `Register({ motherLode })` rejects with
|
|
87
|
+
`CJS_RESMAN_ACTIVE_RESOURCE_OPERATIONS` while queued or direct mutations are
|
|
88
|
+
active, including a reload candidate. Normal `Wait()` drains queued roots and
|
|
89
|
+
candidate lineages; a direct caller must await its own load/prepare promise
|
|
90
|
+
before retrying replacement. Canonical and candidate authority prevent late
|
|
91
|
+
publication, but started source or format work is not yet aborted;
|
|
92
|
+
deterministic cleanup applies to the staged candidate resource itself.
|
|
93
|
+
|
|
94
|
+
## Divergence from Carbon
|
|
95
|
+
|
|
96
|
+
This availability contract intentionally differs from Carbon.
|
|
97
|
+
`BlueAsyncRes::Reload` cancels/joins work, releases dependent cached data, and
|
|
98
|
+
reloads the same canonical object in place; failure can therefore leave that
|
|
99
|
+
stable handle bad. Carbon MotherLode replacement also switches immediately and
|
|
100
|
+
has no prepare-success gate or rollback. Runtime-resource instead preserves
|
|
101
|
+
the last published good handle until a distinct candidate has succeeded, and
|
|
102
|
+
never silently retargets existing JavaScript references.
|
|
103
|
+
|
|
104
|
+
## Related documentation
|
|
105
|
+
|
|
106
|
+
- [MotherLode identity, cache, and retention](motherlode-cache.md)
|
|
107
|
+
- [Queues and the Wait fence](queues.md)
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Texture arrays and update generations
|
|
2
|
+
|
|
3
|
+
Status: Evolving
|
|
4
|
+
Scope: `@carbonenginejs/runtime-resource`
|
|
5
|
+
Audience: Users and integrators
|
|
6
|
+
Summary: Defines the texture-array proxy surface, the requested/prepared revision boundary, and the adapter commit contract.
|
|
7
|
+
|
|
8
|
+
## Layer proxies
|
|
9
|
+
|
|
10
|
+
Texture-array resources expose one ordinary-looking proxy per ordered layer:
|
|
11
|
+
|
|
12
|
+
```js
|
|
13
|
+
const textureArray = new CjsTextureArrayRes({
|
|
14
|
+
paths: [
|
|
15
|
+
"res:/detail1.dds",
|
|
16
|
+
"res:/detail2.dds",
|
|
17
|
+
"res:/detail3.dds"
|
|
18
|
+
],
|
|
19
|
+
layerNames: [ "Detail1Map", "Detail2Map", "Detail3Map" ],
|
|
20
|
+
updateScheduler: resource => frameQueue.add(resource)
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const detail2 = textureArray.GetLayerParameter(1);
|
|
24
|
+
detail2.SetValue("res:/replacement.dds");
|
|
25
|
+
|
|
26
|
+
detail2.textureRes === textureArray; // true
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Proxy setters only update their source path and invalidate the parent. The
|
|
30
|
+
parent is scheduled once even if several proxies change in the same frame.
|
|
31
|
+
The next-frame consumer calls `Update()` or `ConsumeUpdateRequest()` to
|
|
32
|
+
obtain one immutable ordered snapshot. Runtime-resource does not know which
|
|
33
|
+
shader metadata caused the aggregate request; shader packages and engine
|
|
34
|
+
adapters map public parameter names to layer indices.
|
|
35
|
+
|
|
36
|
+
Public effect parameters remain separate from these internal proxies. Their
|
|
37
|
+
authored paths and individual 2D source resources are not replaced by the
|
|
38
|
+
aggregate. An engine-owned, non-persisted bridge mirrors public changes into
|
|
39
|
+
the fixed internal layers.
|
|
40
|
+
|
|
41
|
+
## Update generations
|
|
42
|
+
|
|
43
|
+
`CjsTextureArrayRes` is a derived multi-source resource with an explicit
|
|
44
|
+
requested/prepared revision boundary:
|
|
45
|
+
|
|
46
|
+
```text
|
|
47
|
+
proxy/source change
|
|
48
|
+
-> requested revision + dirty layer
|
|
49
|
+
-> one scheduled next-frame snapshot
|
|
50
|
+
-> consumed/in-flight request
|
|
51
|
+
-> adapter candidate preparation
|
|
52
|
+
-> guarded adapter + prepared-revision publication
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`ConsumeUpdateRequest()` produces an immutable snapshot and marks that
|
|
56
|
+
revision in flight. A current consumed revision may be completed through
|
|
57
|
+
`CommitPreparedAdapterRevision()`, failed through `FailUpdateRequest()`, or
|
|
58
|
+
returned to the queue through `RetryUpdateRequest()`. Commit-before-consume
|
|
59
|
+
and stale commits are rejected; rejected candidate allocations are destroyed
|
|
60
|
+
by default.
|
|
61
|
+
|
|
62
|
+
Publication installs the adapter allocation and prepared revision before
|
|
63
|
+
completion events run. The result returns the displaced allocation to the
|
|
64
|
+
adapter owner for post-publication destruction. A reentrant source change may
|
|
65
|
+
therefore request a newer revision without allowing stale completion to
|
|
66
|
+
replace it. The previous prepared allocation and `IsGood()` remain usable
|
|
67
|
+
while a replacement is pending or if replacement preparation fails.
|
|
68
|
+
|
|
69
|
+
`Ready()` is specialized for this derived resource: it resolves when the
|
|
70
|
+
generation requested at call time has been published, rather than delegating
|
|
71
|
+
to a single-source object loader. Initial preparation failure rejects it.
|
|
72
|
+
|
|
73
|
+
## Adapter commit example
|
|
74
|
+
|
|
75
|
+
Consumed snapshots are explicit in-flight generations. An adapter either
|
|
76
|
+
publishes the current candidate atomically, requeues retryable work, or
|
|
77
|
+
records failure:
|
|
78
|
+
|
|
79
|
+
```js
|
|
80
|
+
const request = textureArray.ConsumeUpdateRequest();
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
const candidate = await adapter.PrepareTextureArray(request);
|
|
84
|
+
const result = textureArray.CommitPreparedAdapterRevision(
|
|
85
|
+
request.revision,
|
|
86
|
+
"webgpu",
|
|
87
|
+
candidate
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
// A rejected/stale candidate is destroyed by the commit method by default.
|
|
91
|
+
// The adapter owns disposal of a successfully displaced allocation.
|
|
92
|
+
result.displaced?.destroy();
|
|
93
|
+
} catch (error) {
|
|
94
|
+
textureArray.FailUpdateRequest(request.revision, error, { retry: true });
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
await textureArray.Ready(); // the generation requested at call time
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Sources and topology
|
|
101
|
+
|
|
102
|
+
Logical paths and resolved sources are independent. `SetLayerResource()`
|
|
103
|
+
attaches a resolved or LOD-specific source without rewriting the logical
|
|
104
|
+
requested path or persistence. `TouchLayer()` invalidates an in-place source
|
|
105
|
+
revision. `HandleAdapterLoss()` drops an unusable adapter allocation and
|
|
106
|
+
schedules a complete topology rebuild. Topology-changing snapshots set
|
|
107
|
+
`topologyChanged: true` and report only valid current layer indices in
|
|
108
|
+
`dirtyLayers`.
|
|
109
|
+
|
|
110
|
+
## Related documentation
|
|
111
|
+
|
|
112
|
+
- [Architecture and boundaries](../architecture.md)
|
|
113
|
+
- [Texture CPU pipeline and LOD membership](texture-pipeline.md)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Texture CPU pipeline and LOD membership
|
|
2
|
+
|
|
3
|
+
Status: Evolving
|
|
4
|
+
Scope: `@carbonenginejs/runtime-resource`
|
|
5
|
+
Audience: Users and integrators
|
|
6
|
+
Summary: Defines the `Tr2TexturePipeline` CPU-only step contract and `Tr2TextureLodManager` membership ownership.
|
|
7
|
+
|
|
8
|
+
## Tr2TexturePipeline
|
|
9
|
+
|
|
10
|
+
`Tr2TexturePipeline` is the Carbon texture-specific CPU bitmap pipeline, not a
|
|
11
|
+
general resource prepare stage. `GetResourceDependencies()` returns the sorted
|
|
12
|
+
unique paths required by load and channel-pack steps. `Execute()` resolves
|
|
13
|
+
those inputs from an explicit `inputs` map/object, an async `load(path)`
|
|
14
|
+
callback, or an injected `CjsResMan`, then returns a canonical plain
|
|
15
|
+
`rgba8unorm` payload:
|
|
16
|
+
|
|
17
|
+
```js
|
|
18
|
+
import {
|
|
19
|
+
Tr2TexturePipeline,
|
|
20
|
+
Tr2TexturePipelineStepLoad,
|
|
21
|
+
Tr2TexturePipelineStepLimitSize
|
|
22
|
+
} from "@carbonenginejs/runtime-resource";
|
|
23
|
+
|
|
24
|
+
const load = new Tr2TexturePipelineStepLoad();
|
|
25
|
+
load.path = "res:/texture/source.png";
|
|
26
|
+
const limit = new Tr2TexturePipelineStepLimitSize();
|
|
27
|
+
limit.maxWidth = 512;
|
|
28
|
+
|
|
29
|
+
const pipeline = new Tr2TexturePipeline();
|
|
30
|
+
pipeline.steps = [ load, limit ];
|
|
31
|
+
const rgba = await pipeline.Execute(0, 0, { resMan });
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The maintained runtime path currently accepts canonical `rgba8unorm` inputs.
|
|
35
|
+
Load copies the source bitmap, limit-size repeatedly performs a 2x2 CPU
|
|
36
|
+
downsample, pack builds logical RGBA channels from independent inputs, and
|
|
37
|
+
Carbon's present compress step remains validation-only because the native
|
|
38
|
+
method is itself a no-op. Unsupported step types fail explicitly.
|
|
39
|
+
|
|
40
|
+
For the decoded-DDS fallback contract used by 2D texture inputs, see the
|
|
41
|
+
[DDS notes in the format map](../formats/README.md#dds-decoded-fallback).
|
|
42
|
+
|
|
43
|
+
## Tr2TextureLodManager
|
|
44
|
+
|
|
45
|
+
`Tr2TextureLodManager` owns only ordered resource membership through
|
|
46
|
+
`RegisterTexture()`, `UnregisterTexture()`, and `GetManagedTextures()`.
|
|
47
|
+
Engine packages continue to own GPU allocations, upload accounting, device
|
|
48
|
+
budgets, capability limits, and device-loss recovery.
|
|
49
|
+
|
|
50
|
+
## Related documentation
|
|
51
|
+
|
|
52
|
+
- [Texture arrays and update generations](texture-arrays.md)
|
|
53
|
+
- [Format subpaths](../formats/README.md)
|