@carbonenginejs/runtime-resource 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE +1 -1
- package/README.md +67 -537
- package/dist/formats/dds/core/bc6h.js +288 -0
- package/dist/formats/dds/core/bc6h.js.map +1 -0
- package/dist/formats/dds/core/bc7.js +251 -0
- package/dist/formats/dds/core/bc7.js.map +1 -0
- package/dist/formats/dds/core/helpers.js +6 -3
- package/dist/formats/dds/core/helpers.js.map +1 -1
- 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 +7 -0
- package/dist/formats/index.js.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/resources/Tr2TexturePipelineStepGenerateMips.js +22 -0
- package/dist/resources/Tr2TexturePipelineStepGenerateMips.js.map +1 -0
- package/dist/resources/TriGeometryRes.js +24 -1
- package/dist/resources/TriGeometryRes.js.map +1 -1
- package/docs/README.md +69 -0
- package/docs/architecture.md +86 -0
- package/docs/concepts/resource-lifecycle.md +217 -0
- package/docs/formats/README.md +104 -0
- package/{FORMAT-PROVENANCE.md → docs/formats/provenance.md} +34 -5
- package/docs/formats/stl.md +37 -0
- package/docs/formats/wwise.md +44 -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/gr2/LICENSE +21 -0
- package/format-notices/gr2/NOTICE +60 -0
- package/format-notices/gr2/THIRD-PARTY-NOTICES.md +93 -0
- package/package.json +51 -50
- package/resource-lifecycle.md +0 -679
|
@@ -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)
|
package/docs/roadmap.md
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# Roadmap
|
|
2
|
+
|
|
3
|
+
Status: Evolving
|
|
4
|
+
Scope: `@carbonenginejs/runtime-resource`
|
|
5
|
+
Audience: Integrators and maintainers
|
|
6
|
+
Summary: Records approved future direction and open design questions; nothing on this page is implemented.
|
|
7
|
+
|
|
8
|
+
Everything below is future work. Current behavior is documented in the
|
|
9
|
+
[reference pages](reference/motherlode-cache.md); where this page and a
|
|
10
|
+
reference page disagree, the reference page describes the shipped package.
|
|
11
|
+
|
|
12
|
+
## Runtime manager follow-up
|
|
13
|
+
|
|
14
|
+
The ResMan/MotherLode contract is complete for its current consumers. Future
|
|
15
|
+
work should be driven by measured application needs rather than another
|
|
16
|
+
resource preparation abstraction:
|
|
17
|
+
|
|
18
|
+
- main-queue priority and starvation policy;
|
|
19
|
+
- cancellation/abort propagation for work that has already started;
|
|
20
|
+
- `WaitUrgent()` after real priority and bounded fairness;
|
|
21
|
+
- queue-time and reader/format-time telemetry;
|
|
22
|
+
- application-level default retention policy selection;
|
|
23
|
+
- automatic resource/payload byte estimation and separate CPU/adapter
|
|
24
|
+
budgets;
|
|
25
|
+
- browser-aware source behavior such as fetch response type selection;
|
|
26
|
+
- purged-resource/device-loss recovery policy (backend device-loss recovery
|
|
27
|
+
belongs to the engine's realization operation).
|
|
28
|
+
|
|
29
|
+
## Pre-adoption lifecycle API cleanup (approved, not implemented)
|
|
30
|
+
|
|
31
|
+
No released consumer currently depends on the ccpwgl-compatible liveness
|
|
32
|
+
names; the known ccpwgl format integrations are migration targets rather than
|
|
33
|
+
a reason to preserve them. Before runtime-core or another public consumer
|
|
34
|
+
adopts this contract, remove `KeepAlive()` and `KeepPayloadAlive()` instead
|
|
35
|
+
of retaining or deprecating compatibility aliases.
|
|
36
|
+
|
|
37
|
+
The public lifecycle should express caller intent directly:
|
|
38
|
+
|
|
39
|
+
- `Ready()` obtains or reconstructs the CPU payload;
|
|
40
|
+
- `AcquireLock()` returns a scoped hard-retention token;
|
|
41
|
+
- `ReleasePayload()` explicitly drops the CPU payload; and
|
|
42
|
+
- state queries remain pure.
|
|
43
|
+
|
|
44
|
+
ResMan should update identity activity when a canonical resource is acquired
|
|
45
|
+
and payload activity when it publishes or returns a ready payload. Those
|
|
46
|
+
timestamps are cache-policy implementation details, not calls consumers
|
|
47
|
+
should have to make. Cache admission and promotion must also be explicit
|
|
48
|
+
manager operations: merely accessing a resource must not permanently move it
|
|
49
|
+
from the byte-budget candidate set into an unbudgeted live set.
|
|
50
|
+
|
|
51
|
+
Do not add `TouchIdentity()` or `TouchPayload()` to the initial public API
|
|
52
|
+
unless a concrete soft-retention consumer appears. A consumer that needs a
|
|
53
|
+
residency guarantee should acquire a lock; one that merely uses a resource
|
|
54
|
+
should call `Ready()` and allow the configured cache policy to operate.
|
|
55
|
+
|
|
56
|
+
Raw `Lock()` / `Unlock()` is easy to mis-pair across asynchronous success,
|
|
57
|
+
failure, cancellation, and disposal. Prefer a JS-only acquired-lock API:
|
|
58
|
+
|
|
59
|
+
```js
|
|
60
|
+
const hold = resource.AcquireLock();
|
|
61
|
+
try {
|
|
62
|
+
const payload = await resource.Ready();
|
|
63
|
+
await consume(payload);
|
|
64
|
+
} finally {
|
|
65
|
+
hold.Release();
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The acquired token should add exactly one lock, capture the canonical key and
|
|
70
|
+
ownership generation that received it, and release that exact lock at most
|
|
71
|
+
once. `Release()` must be idempotent. If the original record has been
|
|
72
|
+
deleted, replaced, purged, or rebound, releasing the stale token must not
|
|
73
|
+
decrement a new owner's lock. Acquisition and release must never fetch,
|
|
74
|
+
reconstruct, prepare, or release payload data themselves.
|
|
75
|
+
|
|
76
|
+
The token is an async-safety replacement for exposing the existing raw lock
|
|
77
|
+
count, not a new retention policy. For example, runtime-audio can hold
|
|
78
|
+
source/PCM payload through decode and release the token once it owns the
|
|
79
|
+
resulting WebAudio `AudioBuffer`; keeping the resource locked for the full
|
|
80
|
+
playback or decoded-cache lifetime would unnecessarily retain both
|
|
81
|
+
representations. Runtime-lifetime and group retention remain caller policy
|
|
82
|
+
built from explicit tokens, not an implicit default on every loaded resource.
|
|
83
|
+
|
|
84
|
+
## Open design questions
|
|
85
|
+
|
|
86
|
+
- Should `Unload()` drop only adapter payloads by default, or CPU payloads
|
|
87
|
+
too?
|
|
88
|
+
- Should there be explicit `UnloadAdapterResources()`, `UnloadPayload()`, and
|
|
89
|
+
`Purge()` phases?
|
|
90
|
+
- Which format/resource-specific estimators should supply separate identity,
|
|
91
|
+
CPU-payload, and adapter byte weights without double-counting shared
|
|
92
|
+
buffers?
|
|
93
|
+
- Should manually attached/dynamic resources default to locked, like ccpwgl's
|
|
94
|
+
manual shader resources use `doNotPurge`?
|
|
95
|
+
- What explicit `Reload()`/reconstruction API should restore purged resources
|
|
96
|
+
without introducing surprising browser or network work?
|
|
97
|
+
- A resource-level `Purge()`/`Reload()` vocabulary remains future policy
|
|
98
|
+
work, as does whether `Unload()` should release engine adapter resources
|
|
99
|
+
and optionally CPU payloads.
|
|
100
|
+
|
|
101
|
+
## Related documentation
|
|
102
|
+
|
|
103
|
+
- [MotherLode identity, cache, and retention](reference/motherlode-cache.md)
|
|
104
|
+
- [Candidate-first atomic reload](reference/reload.md)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 cppctamber / CarbonEngineJS contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
carbonenginejs/format-gr2
|
|
2
|
+
=========================
|
|
3
|
+
|
|
4
|
+
A pure-JavaScript reader for RAD Game Tools' Granny 3D (.gr2) file format.
|
|
5
|
+
Contains no RAD/Granny code and does not link granny2.dll. Section decompression
|
|
6
|
+
is reimplemented from clean-room / open reverse-engineering efforts, listed below.
|
|
7
|
+
|
|
8
|
+
CarbonEngineJS-facing code is copyright cppctamber / CarbonEngineJS
|
|
9
|
+
contributors. CarbonEngine source material, formats, class names, and schema
|
|
10
|
+
semantics remain copyright their original CarbonEngine authors.
|
|
11
|
+
Fenris Creations (CCP Games) source material, tools, assets, shader behavior,
|
|
12
|
+
and EVE-related data remain copyright their original holders.
|
|
13
|
+
|
|
14
|
+
Current repository license: MIT. See LICENSE.
|
|
15
|
+
|
|
16
|
+
The former EUPL-derived BitKnit implementation was replaced on 2026-07-24 by
|
|
17
|
+
an original clean-room implementation written solely from this package's
|
|
18
|
+
published format specification (docs/formats/bitknit2.md); no EUPL-derived
|
|
19
|
+
code remains.
|
|
20
|
+
|
|
21
|
+
Provenance and legal requirements
|
|
22
|
+
---------------------------------
|
|
23
|
+
|
|
24
|
+
When this package copies, ports, translates, or derives from CarbonEngine,
|
|
25
|
+
ccpwgl, Fenris Creations (CCP Games) / EVE assets or tools, RAD/Granny
|
|
26
|
+
materials, or any third-party library, the relevant section below must record
|
|
27
|
+
the upstream copyright holders, project/repository, file path, revision when
|
|
28
|
+
known, license name, and required redistribution obligations. Behavioral
|
|
29
|
+
references with no copied expression should be marked as references only.
|
|
30
|
+
|
|
31
|
+
CarbonEngine and Fenris Creations (CCP Games) are mentioned for interoperability
|
|
32
|
+
and provenance context. This package is not affiliated with or endorsed by CCP
|
|
33
|
+
Games.
|
|
34
|
+
|
|
35
|
+
Third-party algorithm provenance
|
|
36
|
+
--------------------------------
|
|
37
|
+
|
|
38
|
+
Oodle1 / Oodle0 section decompressor (src/codecs/oodle1.js)
|
|
39
|
+
Ported from the open Granny2 decompressors:
|
|
40
|
+
- nwn2mdk "gr2_decompress" (Boost Software License 1.0)
|
|
41
|
+
- arves100/opengr2 "oodle1.c" (MPL-2.0) https://github.com/arves100/opengr2
|
|
42
|
+
This is Granny's own legacy codec, not RAD's modern Oodle (Kraken/BitKnit families).
|
|
43
|
+
|
|
44
|
+
BitKnit / BitKnit2 section decompressor (src/core/bitknit2.js)
|
|
45
|
+
Original CarbonEngineJS clean-room implementation (2026-07-24), written
|
|
46
|
+
solely from this package's published format specification
|
|
47
|
+
(docs/formats/bitknit2.md) by an isolated implementer, and validated
|
|
48
|
+
byte-exact against the EVE .gr2 corpus. It replaced a prior port of
|
|
49
|
+
neptuwunium/Knit (EUPL-1.2); no code from Knit, pybg3, ooz, or any other
|
|
50
|
+
BitKnit implementation remains. See THIRD-PARTY-NOTICES.md.
|
|
51
|
+
|
|
52
|
+
Granny curve decompression (src/curves/**, optional; enabled via decompressCurves)
|
|
53
|
+
Adapted from cppctamber's granny extension for ccpwgl2 (src/core/reader/granny/curves).
|
|
54
|
+
|
|
55
|
+
Container / relocation / type-tree reflection (src/container.js, src/reader.js)
|
|
56
|
+
Original to this project, derived from the on-disk Granny GRN format layout
|
|
57
|
+
(cross-referenced with the Granny SDK headers and the above projects' docs).
|
|
58
|
+
|
|
59
|
+
Please retain this NOTICE, LICENSE, THIRD-PARTY-NOTICES.md, and the upstream
|
|
60
|
+
licenses when redistributing.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Third-party notices
|
|
2
|
+
|
|
3
|
+
`format-gr2` is licensed under **MIT**. It contains no RAD/Granny proprietary
|
|
4
|
+
code and does not link `granny2.dll`. The section decompressors are original
|
|
5
|
+
or ported from permissively licensed open reverse-engineering work and must
|
|
6
|
+
retain the notices and obligations listed below.
|
|
7
|
+
|
|
8
|
+
Do not treat copied or ported prior work as CarbonEngineJS-original code. Each
|
|
9
|
+
copied/ported/adapted component must list its upstream copyright holders,
|
|
10
|
+
source project, path, revision when known, license, and redistribution
|
|
11
|
+
requirements. Code used only as a behavioral reference should be marked as
|
|
12
|
+
reference-only.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Oodle1 / Oodle0 decompressor - `src/core/oodle1.js`
|
|
17
|
+
|
|
18
|
+
**Boost Software License 1.0.** Ported from the Granny `gr2_decompress` implementation
|
|
19
|
+
shipped in [Arbos/nwn2mdk](https://github.com/Arbos/nwn2mdk) (the decompressor is
|
|
20
|
+
originally by *berenm* and is distributed under Boost 1.0, separate from nwn2mdk's
|
|
21
|
+
Apache-2.0 format code). Cross-checked for correctness against
|
|
22
|
+
[arves100/opengr2](https://github.com/arves100/opengr2) `oodle1.c` (MPL-2.0) - used as a
|
|
23
|
+
verification reference only; no opengr2 code was copied, so MPL-2.0 does not apply.
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
Boost Software License - Version 1.0 - August 17th, 2003
|
|
27
|
+
|
|
28
|
+
Permission is hereby granted, free of charge, to any person or organization
|
|
29
|
+
obtaining a copy of the software and accompanying documentation covered by
|
|
30
|
+
this license (the "Software") to use, reproduce, display, distribute, execute,
|
|
31
|
+
and transmit the Software, and to prepare derivative works of the Software, and
|
|
32
|
+
to permit third-parties to whom the Software is furnished to do so, all subject
|
|
33
|
+
to the following:
|
|
34
|
+
|
|
35
|
+
The copyright notices in the Software and this entire statement, including the
|
|
36
|
+
above license grant, this restriction and the following disclaimer, must be
|
|
37
|
+
included in all copies of the Software, in whole or in part, and all derivative
|
|
38
|
+
works of the Software, unless such copies or derivative works are solely in the
|
|
39
|
+
form of machine-executable object code generated by a source language processor.
|
|
40
|
+
|
|
41
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
42
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
43
|
+
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
|
44
|
+
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
|
45
|
+
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
|
46
|
+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
47
|
+
DEALINGS IN THE SOFTWARE.
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## BitKnit / BitKnit2 decompressor - `src/core/bitknit2.js`
|
|
53
|
+
|
|
54
|
+
**Original CarbonEngineJS code (MIT).** On 2026-07-24 the previous
|
|
55
|
+
EUPL-derived port (transcribed from
|
|
56
|
+
[neptuwunium/Knit](https://github.com/neptuwunium/Knit)
|
|
57
|
+
`GrannyBitKnitCompression.cs`, EUPL-1.2) was replaced by a clean-room
|
|
58
|
+
implementation produced through a documented two-party process: one party
|
|
59
|
+
analyzed the format and published a facts-only decoding specification
|
|
60
|
+
(`docs/formats/bitknit2.md`); a second, isolated party implemented the
|
|
61
|
+
decoder solely from that specification with no access to Knit, pybg3, ooz,
|
|
62
|
+
or any other BitKnit implementation. The result was validated byte-exact
|
|
63
|
+
against 539 BitKnit2 streams extracted from the EVE `.gr2` corpus (section
|
|
64
|
+
payloads and compressed pointer-fixup blocks) plus synthetic raw-quantum
|
|
65
|
+
streams. No third-party code, EUPL-derived or otherwise, remains in this
|
|
66
|
+
file. BitKnit is a RAD Game Tools codec; the specification records on-disk
|
|
67
|
+
format facts only.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Granny animation-curve decompression - `src/core/curves.js`
|
|
72
|
+
|
|
73
|
+
Authored by **cppctamber** as part of ccpwgl2 (`src/core/reader/granny/curves`), the
|
|
74
|
+
same author/owner as this library; included here under this project's MIT
|
|
75
|
+
license.
|
|
76
|
+
|
|
77
|
+
## Tangent-frame shader math - `src/core/tangents.js`
|
|
78
|
+
|
|
79
|
+
The packed tangent-frame decode/encode math is derived from Fenris Creations
|
|
80
|
+
(formerly CCP / CCP Games) EVE/Carbon shader behavior for packed tangent frames.
|
|
81
|
+
This package contains a JavaScript implementation of the tangent-frame math and
|
|
82
|
+
does not include copied shader source.
|
|
83
|
+
|
|
84
|
+
If shader source is later copied, ported, or translated directly, update this
|
|
85
|
+
notice with the exact upstream repository, file path, revision, copyright
|
|
86
|
+
holders, license text or required notice location, and redistribution
|
|
87
|
+
obligations before committing that work.
|
|
88
|
+
|
|
89
|
+
## Container, relocation, type-tree reflection, emitter
|
|
90
|
+
|
|
91
|
+
Original to this project (cppctamber / carbonenginejs). Included here under
|
|
92
|
+
this project's MIT license. Derived from the public on-disk Granny
|
|
93
|
+
GRN format layout and the Granny SDK header type definitions.
|
package/package.json
CHANGED
|
@@ -1,51 +1,52 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@carbonenginejs/runtime-resource",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "CarbonEngineJS resource lifecycle, cache, source, and object loading contracts.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"exports": {
|
|
7
|
-
".": "./dist/index.js",
|
|
8
|
-
"./formats": "./dist/formats/index.js",
|
|
9
|
-
"./formats/black": "./dist/formats/black/index.js",
|
|
10
|
-
"./formats/black/schema": "./dist/formats/black/core/blackSchema.js",
|
|
11
|
-
"./formats/black/enums": "./dist/formats/black/core/blackEnums.js",
|
|
12
|
-
"./formats/black/version": "./dist/formats/black/core/blackVersion.js",
|
|
13
|
-
"./formats/bnk": "./dist/formats/bnk/index.js",
|
|
14
|
-
"./formats/cmf": "./dist/formats/cmf/index.js",
|
|
15
|
-
"./formats/dds": "./dist/formats/dds/index.js",
|
|
16
|
-
"./formats/fbx": "./dist/formats/fbx/index.js",
|
|
17
|
-
"./formats/flac": "./dist/formats/flac/index.js",
|
|
18
|
-
"./formats/gif": "./dist/formats/gif/index.js",
|
|
19
|
-
"./formats/gltf": "./dist/formats/gltf/index.js",
|
|
20
|
-
"./formats/
|
|
21
|
-
"./formats/
|
|
22
|
-
"./formats/
|
|
23
|
-
"./formats/
|
|
24
|
-
"./formats/
|
|
25
|
-
"./formats/
|
|
26
|
-
"./formats/
|
|
27
|
-
"./formats/red
|
|
28
|
-
"./formats/
|
|
29
|
-
"./formats/
|
|
30
|
-
"./formats/
|
|
31
|
-
"./formats/
|
|
32
|
-
"./formats/
|
|
33
|
-
"./formats/
|
|
34
|
-
"./formats/
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@carbonenginejs/runtime-resource",
|
|
3
|
+
"version": "0.9.0",
|
|
4
|
+
"description": "CarbonEngineJS resource lifecycle, cache, source, and object loading contracts.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": "./dist/index.js",
|
|
8
|
+
"./formats": "./dist/formats/index.js",
|
|
9
|
+
"./formats/black": "./dist/formats/black/index.js",
|
|
10
|
+
"./formats/black/schema": "./dist/formats/black/core/blackSchema.js",
|
|
11
|
+
"./formats/black/enums": "./dist/formats/black/core/blackEnums.js",
|
|
12
|
+
"./formats/black/version": "./dist/formats/black/core/blackVersion.js",
|
|
13
|
+
"./formats/bnk": "./dist/formats/bnk/index.js",
|
|
14
|
+
"./formats/cmf": "./dist/formats/cmf/index.js",
|
|
15
|
+
"./formats/dds": "./dist/formats/dds/index.js",
|
|
16
|
+
"./formats/fbx": "./dist/formats/fbx/index.js",
|
|
17
|
+
"./formats/flac": "./dist/formats/flac/index.js",
|
|
18
|
+
"./formats/gif": "./dist/formats/gif/index.js",
|
|
19
|
+
"./formats/gltf": "./dist/formats/gltf/index.js",
|
|
20
|
+
"./formats/gr2": "./dist/formats/gr2/index.js",
|
|
21
|
+
"./formats/jpeg": "./dist/formats/jpeg/index.js",
|
|
22
|
+
"./formats/mp3": "./dist/formats/mp3/index.js",
|
|
23
|
+
"./formats/mp4": "./dist/formats/mp4/index.js",
|
|
24
|
+
"./formats/obj": "./dist/formats/obj/index.js",
|
|
25
|
+
"./formats/ogg": "./dist/formats/ogg/index.js",
|
|
26
|
+
"./formats/png": "./dist/formats/png/index.js",
|
|
27
|
+
"./formats/red": "./dist/formats/red/index.js",
|
|
28
|
+
"./formats/red/schema": "./dist/formats/red/core/blackDefinitions.js",
|
|
29
|
+
"./formats/stl": "./dist/formats/stl/index.js",
|
|
30
|
+
"./formats/tga": "./dist/formats/tga/index.js",
|
|
31
|
+
"./formats/wav": "./dist/formats/wav/index.js",
|
|
32
|
+
"./formats/webm": "./dist/formats/webm/index.js",
|
|
33
|
+
"./formats/webp": "./dist/formats/webp/index.js",
|
|
34
|
+
"./formats/wem": "./dist/formats/wem/index.js",
|
|
35
|
+
"./formats/yaml": "./dist/formats/yaml/index.js"
|
|
36
|
+
},
|
|
37
|
+
"sideEffects": false,
|
|
38
|
+
"engines": {
|
|
39
|
+
"node": ">=18"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@carbonenginejs/core-math": "^0.1.5",
|
|
42
43
|
"@carbonenginejs/core-types": "^0.12.0",
|
|
43
|
-
"@carbonenginejs/runtime-const": "^0.1.0",
|
|
44
|
-
"meshoptimizer": "^1.2.0",
|
|
45
|
-
"yaml": "^2.4.0"
|
|
46
|
-
},
|
|
47
|
-
"license": "MIT",
|
|
48
|
-
"publishConfig": {
|
|
49
|
-
"access": "public"
|
|
50
|
-
}
|
|
51
|
-
}
|
|
44
|
+
"@carbonenginejs/runtime-const": "^0.1.0",
|
|
45
|
+
"meshoptimizer": "^1.2.0",
|
|
46
|
+
"yaml": "^2.4.0"
|
|
47
|
+
},
|
|
48
|
+
"license": "MIT",
|
|
49
|
+
"publishConfig": {
|
|
50
|
+
"access": "public"
|
|
51
|
+
}
|
|
52
|
+
}
|