@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.
Files changed (297) hide show
  1. package/LICENSE +21 -21
  2. package/NOTICE +31 -29
  3. package/README.md +67 -548
  4. package/dist/CjsMotherLode.js +1271 -1271
  5. package/dist/CjsResMan.js +3005 -3005
  6. package/dist/CjsResManQueue.js +214 -214
  7. package/dist/CjsResource.js +566 -566
  8. package/dist/CjsResourceSource.js +68 -59
  9. package/dist/CjsResourceSource.js.map +1 -1
  10. package/dist/_virtual/_rollupPluginBabelHelpers.js +153 -153
  11. package/dist/format/CjsBlueReader.js +269 -269
  12. package/dist/format/CjsFormat.js +197 -192
  13. package/dist/format/CjsFormat.js.map +1 -1
  14. package/dist/format/CjsReader.js +18 -18
  15. package/dist/format/CjsResourceProbe.js +277 -277
  16. package/dist/format/payloadContract.js +173 -173
  17. package/dist/formats/black/CjsBlackFormat.js +278 -278
  18. package/dist/formats/black/core/CjsBlackBinaryReader.js +157 -153
  19. package/dist/formats/black/core/CjsBlackBinaryReader.js.map +1 -1
  20. package/dist/formats/black/core/CjsBlackPropertyReaders.js +387 -382
  21. package/dist/formats/black/core/CjsBlackPropertyReaders.js.map +1 -1
  22. package/dist/formats/black/core/CjsBlackReader.js +639 -639
  23. package/dist/formats/black/core/CjsBlackSchemaRegistry.js +437 -433
  24. package/dist/formats/black/core/CjsBlackSchemaRegistry.js.map +1 -1
  25. package/dist/formats/black/core/black-schema-v1-2026-07-23.json.js +4 -0
  26. package/dist/formats/black/core/black-schema-v1-2026-07-23.json.js.map +1 -0
  27. package/dist/formats/black/core/blackConstants.js +7 -7
  28. package/dist/formats/black/core/blackDefinitions.js +10 -10
  29. package/dist/formats/black/core/blackDefinitions.js.map +1 -1
  30. package/dist/formats/black/core/blackEnums.js +6 -6
  31. package/dist/formats/black/core/blackSchema.js +3 -3
  32. package/dist/formats/black/core/blackVersion.js +22 -22
  33. package/dist/formats/black/core/helpers.js +182 -182
  34. package/dist/formats/black/core/schema.js +4 -4
  35. package/dist/formats/black/index.js +2 -2
  36. package/dist/formats/bnk/CjsBnkFormat.js +125 -125
  37. package/dist/formats/bnk/core/graph.js +140 -140
  38. package/dist/formats/bnk/core/helpers.js +409 -409
  39. package/dist/formats/bnk/core/musicNodes.js +493 -489
  40. package/dist/formats/bnk/core/musicNodes.js.map +1 -1
  41. package/dist/formats/bnk/core/soundbanksInfo.js +246 -246
  42. package/dist/formats/bnk/index.js +2 -2
  43. package/dist/formats/cmf/CjsCmfFormat.js +497 -497
  44. package/dist/formats/cmf/core/binary.js +123 -118
  45. package/dist/formats/cmf/core/binary.js.map +1 -1
  46. package/dist/formats/cmf/core/buffers.js +233 -233
  47. package/dist/formats/cmf/core/constants.js +47 -47
  48. package/dist/formats/cmf/core/gr2Anim.js +453 -453
  49. package/dist/formats/cmf/core/helpers.js +287 -287
  50. package/dist/formats/cmf/core/pack.js +276 -276
  51. package/dist/formats/cmf/core/schema.js +364 -364
  52. package/dist/formats/cmf/core/shared.js +268 -268
  53. package/dist/formats/cmf/core/writer.js +517 -517
  54. package/dist/formats/cmf/index.js +2 -2
  55. package/dist/formats/dds/CjsDdsFormat.js +200 -193
  56. package/dist/formats/dds/CjsDdsFormat.js.map +1 -1
  57. package/dist/formats/dds/core/bc6h.js +288 -288
  58. package/dist/formats/dds/core/bc7.js +256 -251
  59. package/dist/formats/dds/core/bc7.js.map +1 -1
  60. package/dist/formats/dds/core/helpers.js +815 -815
  61. package/dist/formats/dds/index.js +2 -2
  62. package/dist/formats/fbx/CjsFbxFormat.js +266 -266
  63. package/dist/formats/fbx/core/helpers.js +3911 -3901
  64. package/dist/formats/fbx/core/helpers.js.map +1 -1
  65. package/dist/formats/fbx/index.js +2 -2
  66. package/dist/formats/flac/CjsFlacFormat.js +93 -87
  67. package/dist/formats/flac/CjsFlacFormat.js.map +1 -1
  68. package/dist/formats/flac/core/helpers.js +295 -295
  69. package/dist/formats/flac/index.js +2 -2
  70. package/dist/formats/gif/CjsGifFormat.js +92 -87
  71. package/dist/formats/gif/CjsGifFormat.js.map +1 -1
  72. package/dist/formats/gif/core/helpers.js +360 -360
  73. package/dist/formats/gif/index.js +2 -2
  74. package/dist/formats/gltf/CjsGltfFormat.js +290 -290
  75. package/dist/formats/gltf/core/helpers.js +292 -292
  76. package/dist/formats/gltf/core/json.js +79 -79
  77. package/dist/formats/gltf/core/parser.js +666 -666
  78. package/dist/formats/gltf/core/targets.js +163 -163
  79. package/dist/formats/gltf/index.js +2 -2
  80. package/dist/formats/gr2/CjsGr2Format.js +46 -0
  81. package/dist/formats/gr2/CjsGr2Format.js.map +1 -0
  82. package/dist/formats/gr2/core/CjsFormatGr2.js +273 -0
  83. package/dist/formats/gr2/core/CjsFormatGr2.js.map +1 -0
  84. package/dist/formats/gr2/core/bitknit2.js +280 -0
  85. package/dist/formats/gr2/core/bitknit2.js.map +1 -0
  86. package/dist/formats/gr2/core/curves.js +1047 -0
  87. package/dist/formats/gr2/core/curves.js.map +1 -0
  88. package/dist/formats/gr2/core/gsf.js +72 -0
  89. package/dist/formats/gr2/core/gsf.js.map +1 -0
  90. package/dist/formats/gr2/core/helpers.js +332 -0
  91. package/dist/formats/gr2/core/helpers.js.map +1 -0
  92. package/dist/formats/gr2/core/json.js +622 -0
  93. package/dist/formats/gr2/core/json.js.map +1 -0
  94. package/dist/formats/gr2/core/oodle1.js +388 -0
  95. package/dist/formats/gr2/core/oodle1.js.map +1 -0
  96. package/dist/formats/gr2/core/reader.js +617 -0
  97. package/dist/formats/gr2/core/reader.js.map +1 -0
  98. package/dist/formats/gr2/core/tangents.js +48 -0
  99. package/dist/formats/gr2/core/tangents.js.map +1 -0
  100. package/dist/formats/gr2/core/targets.js +351 -0
  101. package/dist/formats/gr2/core/targets.js.map +1 -0
  102. package/dist/formats/gr2/index.js +3 -0
  103. package/dist/formats/gr2/index.js.map +1 -0
  104. package/dist/formats/index.js +30 -23
  105. package/dist/formats/index.js.map +1 -1
  106. package/dist/formats/jpeg/CjsJpegFormat.js +212 -206
  107. package/dist/formats/jpeg/CjsJpegFormat.js.map +1 -1
  108. package/dist/formats/jpeg/core/helpers.js +350 -350
  109. package/dist/formats/jpeg/core/jpeg.js +387 -377
  110. package/dist/formats/jpeg/core/jpeg.js.map +1 -1
  111. package/dist/formats/jpeg/index.js +2 -2
  112. package/dist/formats/mp3/CjsMp3Format.js +197 -192
  113. package/dist/formats/mp3/CjsMp3Format.js.map +1 -1
  114. package/dist/formats/mp3/core/helpers.js +338 -338
  115. package/dist/formats/mp3/index.js +2 -2
  116. package/dist/formats/mp4/CjsMp4Format.js +197 -191
  117. package/dist/formats/mp4/CjsMp4Format.js.map +1 -1
  118. package/dist/formats/mp4/core/helpers.js +449 -449
  119. package/dist/formats/mp4/index.js +2 -2
  120. package/dist/formats/obj/CjsObjFormat.js +253 -253
  121. package/dist/formats/obj/core/helpers.js +573 -573
  122. package/dist/formats/obj/core/json.js +64 -64
  123. package/dist/formats/obj/core/parser.js +321 -321
  124. package/dist/formats/obj/index.js +2 -2
  125. package/dist/formats/ogg/CjsOggFormat.js +94 -88
  126. package/dist/formats/ogg/CjsOggFormat.js.map +1 -1
  127. package/dist/formats/ogg/core/helpers.js +387 -387
  128. package/dist/formats/ogg/core/imdct.js +178 -178
  129. package/dist/formats/ogg/core/vorbis.js +1004 -999
  130. package/dist/formats/ogg/core/vorbis.js.map +1 -1
  131. package/dist/formats/ogg/index.js +2 -2
  132. package/dist/formats/png/CjsPngFormat.js +201 -195
  133. package/dist/formats/png/CjsPngFormat.js.map +1 -1
  134. package/dist/formats/png/core/helpers.js +583 -583
  135. package/dist/formats/png/index.js +2 -2
  136. package/dist/formats/red/CjsRedFormat.js +261 -261
  137. package/dist/formats/red/core/CjsRedReader.js +194 -194
  138. package/dist/formats/red/core/blackDefinitions.js +3 -3
  139. package/dist/formats/red/core/helpers.js +139 -139
  140. package/dist/formats/red/core/redGraph.js +68 -68
  141. package/dist/formats/red/core/schema.js +4 -4
  142. package/dist/formats/red/index.js +2 -2
  143. package/dist/formats/stl/CjsStlFormat.js +365 -365
  144. package/dist/formats/stl/core/helpers.js +261 -261
  145. package/dist/formats/stl/core/json.js +51 -51
  146. package/dist/formats/stl/core/stl.js +634 -629
  147. package/dist/formats/stl/core/stl.js.map +1 -1
  148. package/dist/formats/stl/core/targets.js +163 -163
  149. package/dist/formats/stl/index.js +2 -2
  150. package/dist/formats/tga/CjsTgaFormat.js +197 -192
  151. package/dist/formats/tga/CjsTgaFormat.js.map +1 -1
  152. package/dist/formats/tga/core/helpers.js +446 -446
  153. package/dist/formats/tga/index.js +2 -2
  154. package/dist/formats/wav/CjsWavFormat.js +198 -192
  155. package/dist/formats/wav/CjsWavFormat.js.map +1 -1
  156. package/dist/formats/wav/core/helpers.js +328 -328
  157. package/dist/formats/wav/index.js +2 -2
  158. package/dist/formats/webm/CjsWebmFormat.js +197 -191
  159. package/dist/formats/webm/CjsWebmFormat.js.map +1 -1
  160. package/dist/formats/webm/core/helpers.js +537 -537
  161. package/dist/formats/webm/index.js +2 -2
  162. package/dist/formats/webp/CjsWebpFormat.js +91 -86
  163. package/dist/formats/webp/CjsWebpFormat.js.map +1 -1
  164. package/dist/formats/webp/core/helpers.js +214 -214
  165. package/dist/formats/webp/index.js +2 -2
  166. package/dist/formats/wem/CjsWemFormat.js +242 -241
  167. package/dist/formats/wem/CjsWemFormat.js.map +1 -1
  168. package/dist/formats/wem/core/bitStream.js +259 -259
  169. package/dist/formats/wem/core/codebookLibrary.js +164 -164
  170. package/dist/formats/wem/core/helpers.js +417 -417
  171. package/dist/formats/wem/core/packedCodebooksAotuv603.js +30 -30
  172. package/dist/formats/wem/core/ptadpcm.js +77 -77
  173. package/dist/formats/wem/core/resolve.js +121 -121
  174. package/dist/formats/wem/core/wemToOgg.js +485 -485
  175. package/dist/formats/wem/index.js +2 -2
  176. package/dist/formats/yaml/CjsYamlFormat.js +89 -83
  177. package/dist/formats/yaml/CjsYamlFormat.js.map +1 -1
  178. package/dist/formats/yaml/core/CjsYamlReader.js +311 -305
  179. package/dist/formats/yaml/core/CjsYamlReader.js.map +1 -1
  180. package/dist/formats/yaml/core/helpers.js +160 -160
  181. package/dist/formats/yaml/index.js +2 -2
  182. package/dist/index.js +49 -48
  183. package/dist/index.js.map +1 -1
  184. package/dist/resourcePath.js +18 -18
  185. package/dist/resourceStates.js +9 -9
  186. package/dist/resources/AudioGeometryResData.js +47 -47
  187. package/dist/resources/GStateBindingCallbackData.js +31 -31
  188. package/dist/resources/MeshDecalData.js +37 -37
  189. package/dist/resources/MeshDecalLodData.js +34 -34
  190. package/dist/resources/Tr2EffectRes.js +71 -71
  191. package/dist/resources/Tr2GrannyIntersectionResult.js +60 -60
  192. package/dist/resources/Tr2GrannyStateRes.js +44 -44
  193. package/dist/resources/Tr2ImageRes.js +114 -114
  194. package/dist/resources/Tr2LightProfileRes.js +40 -40
  195. package/dist/resources/Tr2MaterialArea.js +34 -34
  196. package/dist/resources/Tr2MaterialMesh.js +31 -31
  197. package/dist/resources/Tr2MaterialRes.js +34 -34
  198. package/dist/resources/Tr2ShaderPermutation.js +43 -43
  199. package/dist/resources/Tr2TextureLodManager.js +80 -80
  200. package/dist/resources/Tr2TextureLodUpdateRequest.js +37 -37
  201. package/dist/resources/Tr2TexturePackChannel.js +37 -37
  202. package/dist/resources/Tr2TexturePipeline.js +52 -52
  203. package/dist/resources/Tr2TexturePipelineParams.js +34 -34
  204. package/dist/resources/Tr2TexturePipelineStepCompress.js +40 -40
  205. package/dist/resources/Tr2TexturePipelineStepGenerateMips.js +22 -0
  206. package/dist/resources/Tr2TexturePipelineStepGenerateMips.js.map +1 -0
  207. package/dist/resources/Tr2TexturePipelineStepLimitSize.js +34 -34
  208. package/dist/resources/Tr2TexturePipelineStepLoad.js +31 -31
  209. package/dist/resources/Tr2TexturePipelineStepPack.js +43 -43
  210. package/dist/resources/TriGeometryRes.js +239 -239
  211. package/dist/resources/TriGeometryResAreaData.js +59 -59
  212. package/dist/resources/TriGeometryResJointData.js +38 -38
  213. package/dist/resources/TriGeometryResLodData.js +88 -88
  214. package/dist/resources/TriGeometryResMeshData.js +63 -63
  215. package/dist/resources/TriGeometryResSkeletonData.js +34 -34
  216. package/dist/resources/TriGrannyRes.js +43 -43
  217. package/dist/resources/TriJointBinding.js +38 -38
  218. package/dist/resources/TriMorphTargetGeometryConstants.js +46 -46
  219. package/dist/resources/TriRtGeometryConstants.js +88 -88
  220. package/dist/resources/TriTextureRes.js +304 -304
  221. package/dist/resources/enums.js +18 -18
  222. package/dist/resources/resourceBoundary.js +47 -47
  223. package/dist/resources/texturePipelineBehavior.js +308 -308
  224. package/dist/texture/CjsTextureArrayRes.js +406 -406
  225. package/dist/texture/CjsTextureParameterProxy.js +133 -133
  226. package/docs/README.md +73 -0
  227. package/docs/architecture.md +86 -0
  228. package/docs/concepts/resource-lifecycle.md +217 -0
  229. package/docs/formats/README.md +105 -0
  230. package/docs/formats/gr2.md +161 -0
  231. package/{FORMAT-PROVENANCE.md → docs/formats/provenance.md} +173 -155
  232. package/docs/formats/stl.md +37 -0
  233. package/docs/formats/wwise.md +44 -0
  234. package/docs/reference/classes/README.md +33 -0
  235. package/docs/reference/classes/core.md +106 -0
  236. package/docs/reference/classes/dropped.md +46 -0
  237. package/docs/reference/classes/formats.md +522 -0
  238. package/docs/reference/classes/resources.md +346 -0
  239. package/docs/reference/classes/texture.md +26 -0
  240. package/docs/reference/events.md +92 -0
  241. package/docs/reference/motherlode-cache.md +244 -0
  242. package/docs/reference/queues.md +102 -0
  243. package/docs/reference/reload.md +107 -0
  244. package/docs/reference/texture-arrays.md +113 -0
  245. package/docs/reference/texture-pipeline.md +53 -0
  246. package/docs/roadmap.md +104 -0
  247. package/format-notices/black/LICENSE +21 -21
  248. package/format-notices/black/NOTICE +47 -47
  249. package/format-notices/bnk/LICENSE +21 -21
  250. package/format-notices/bnk/NOTICE +20 -20
  251. package/format-notices/cmf/LICENSE +21 -21
  252. package/format-notices/cmf/NOTICE +36 -36
  253. package/format-notices/dds/LICENSE +21 -21
  254. package/format-notices/dds/NOTICE +14 -14
  255. package/format-notices/fbx/LICENSE +21 -21
  256. package/format-notices/fbx/NOTICE +14 -14
  257. package/format-notices/flac/LICENSE +21 -21
  258. package/format-notices/flac/NOTICE +14 -14
  259. package/format-notices/gif/LICENSE +21 -21
  260. package/format-notices/gif/NOTICE +14 -14
  261. package/format-notices/gltf/LICENSE +21 -21
  262. package/format-notices/gltf/NOTICE +27 -27
  263. package/format-notices/gr2/LICENSE +21 -0
  264. package/format-notices/gr2/NOTICE +60 -0
  265. package/format-notices/gr2/THIRD-PARTY-NOTICES.md +93 -0
  266. package/format-notices/jpeg/LICENSE +21 -21
  267. package/format-notices/jpeg/NOTICE +14 -14
  268. package/format-notices/mp3/LICENSE +21 -21
  269. package/format-notices/mp3/NOTICE +14 -14
  270. package/format-notices/mp4/LICENSE +21 -21
  271. package/format-notices/mp4/NOTICE +14 -14
  272. package/format-notices/obj/LICENSE +21 -21
  273. package/format-notices/obj/NOTICE +26 -26
  274. package/format-notices/ogg/LICENSE +21 -21
  275. package/format-notices/ogg/NOTICE +28 -28
  276. package/format-notices/png/LICENSE +21 -21
  277. package/format-notices/png/NOTICE +14 -14
  278. package/format-notices/red/LICENSE +21 -21
  279. package/format-notices/red/NOTICE +31 -31
  280. package/format-notices/stl/LICENSE +21 -21
  281. package/format-notices/stl/NOTICE +21 -21
  282. package/format-notices/tga/LICENSE +21 -21
  283. package/format-notices/tga/NOTICE +14 -14
  284. package/format-notices/wav/LICENSE +21 -21
  285. package/format-notices/wav/NOTICE +14 -14
  286. package/format-notices/webm/LICENSE +21 -21
  287. package/format-notices/webm/NOTICE +14 -14
  288. package/format-notices/webp/LICENSE +21 -21
  289. package/format-notices/webp/NOTICE +14 -14
  290. package/format-notices/wem/LICENSE +57 -57
  291. package/format-notices/wem/NOTICE +33 -33
  292. package/format-notices/yaml/LICENSE +21 -21
  293. package/format-notices/yaml/NOTICE +44 -44
  294. package/package.json +52 -51
  295. package/dist/formats/black/core/black-schema-v1-2026-07-11.json.js +0 -4
  296. package/dist/formats/black/core/black-schema-v1-2026-07-11.json.js.map +0 -1
  297. 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)