@flighthq/render 0.3.0-next.1689.2810c05 → 0.3.0-next.1702.8973fda
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/dist/contract.d.ts +1 -0
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +1 -0
- package/dist/contract.js.map +1 -1
- package/dist/explainScene2DCoverage.d.ts +4 -0
- package/dist/explainScene2DCoverage.d.ts.map +1 -0
- package/dist/explainScene2DCoverage.js +57 -0
- package/dist/explainScene2DCoverage.js.map +1 -0
- package/dist/renderRegistryGuards.js +9 -0
- package/dist/renderRegistryGuards.js.map +1 -1
- package/dist/renderer.d.ts.map +1 -1
- package/dist/renderer.js +9 -2
- package/dist/renderer.js.map +1 -1
- package/package.json +10 -10
- package/src/explainScene2DCoverage.test.ts +113 -0
- package/src/renderer.test.ts +24 -1
package/dist/contract.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export * from './renderMaterial';
|
|
|
10
10
|
export * from './renderProxy';
|
|
11
11
|
export * from './renderProxyAdapter';
|
|
12
12
|
export * from './renderQueue';
|
|
13
|
+
export * from './explainScene2DCoverage';
|
|
13
14
|
export * from './renderRegistryGuards';
|
|
14
15
|
export * from './renderRegistrySignals';
|
|
15
16
|
export * from './renderState';
|
package/dist/contract.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAC;AAC1C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,wBAAwB,CAAC;AACvC,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,eAAe,CAAC;AAC9B,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC"}
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAC;AAC1C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,wBAAwB,CAAC;AACvC,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,eAAe,CAAC;AAC9B,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC"}
|
package/dist/contract.js
CHANGED
|
@@ -10,6 +10,7 @@ export * from './renderMaterial';
|
|
|
10
10
|
export * from './renderProxy';
|
|
11
11
|
export * from './renderProxyAdapter';
|
|
12
12
|
export * from './renderQueue';
|
|
13
|
+
export * from './explainScene2DCoverage';
|
|
13
14
|
export * from './renderRegistryGuards';
|
|
14
15
|
export * from './renderRegistrySignals';
|
|
15
16
|
export * from './renderState';
|
package/dist/contract.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAC;AAC1C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,wBAAwB,CAAC;AACvC,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,eAAe,CAAC;AAC9B,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC"}
|
|
1
|
+
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAC;AAC1C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,wBAAwB,CAAC;AACvC,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,eAAe,CAAC;AAC9B,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { RenderState, Scene2DKindUsage, SceneCoverageEntry } from '@flighthq/types/contract';
|
|
2
|
+
export declare function explainScene2DCoverage(out: SceneCoverageEntry[], state: RenderState, usage: Readonly<Scene2DKindUsage>): void;
|
|
3
|
+
export declare function hasScene2DCoverage(state: RenderState, usage: Readonly<Scene2DKindUsage>): boolean;
|
|
4
|
+
//# sourceMappingURL=explainScene2DCoverage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"explainScene2DCoverage.d.ts","sourceRoot":"","sources":["../src/explainScene2DCoverage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAelG,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,kBAAkB,EAAE,EACzB,KAAK,EAAE,WAAW,EAClB,KAAK,EAAE,QAAQ,CAAC,gBAAgB,CAAC,GAChC,IAAI,CAGN;AAID,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,CAAC,gBAAgB,CAAC,GAAG,OAAO,CAEjG"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { RenderRegistry, SceneCoverage } from '@flighthq/types/contract';
|
|
2
|
+
import { getRenderStateRuntime } from './renderState';
|
|
3
|
+
// Clears `out`, then reports every requirement in `usage` that any 2D backend answers the same way,
|
|
4
|
+
// with how well this state is wired for it — satisfied entries included, so one call is a manifest.
|
|
5
|
+
//
|
|
6
|
+
// This covers the two registries that live on the BASE render-state runtime and so mean the same thing
|
|
7
|
+
// on Canvas, DOM, GL and WebGPU: the node-renderer map every backend dispatches through, and the shape
|
|
8
|
+
// command set every backend replays through when it rasterizes. A backend adds its own specifics — GL
|
|
9
|
+
// blend realizations, per-backend material renderers — on top of this, rather than restating it.
|
|
10
|
+
//
|
|
11
|
+
// Unlike the 3D seam, node kinds ARE a requirement here: the 2D pipeline resolves every node through
|
|
12
|
+
// `registerRenderer(state, kind, renderer)`, where the 3D pipeline collects meshes structurally.
|
|
13
|
+
export function explainScene2DCoverage(out, state, usage) {
|
|
14
|
+
out.length = 0;
|
|
15
|
+
collectScene2DCoverageGaps(out, state, usage, false);
|
|
16
|
+
}
|
|
17
|
+
// Whether this state can serve every node kind and shape command `usage` names. Stops at the first
|
|
18
|
+
// shortfall and never allocates. Use the explain form to find out WHICH requirement and how badly.
|
|
19
|
+
export function hasScene2DCoverage(state, usage) {
|
|
20
|
+
return !collectScene2DCoverageGaps(null, state, usage, true);
|
|
21
|
+
}
|
|
22
|
+
// The single implementation both tiers read, so the boolean can never disagree with the explanation.
|
|
23
|
+
// `found` counts only real shortfalls, so appending satisfied entries to `out` never flips the predicate.
|
|
24
|
+
function collectScene2DCoverageGaps(out, state, usage, stopAtFirst) {
|
|
25
|
+
let found = false;
|
|
26
|
+
const runtime = getRenderStateRuntime(state);
|
|
27
|
+
// A node kind with no renderer produces no render proxy, so the node and its whole subtree are
|
|
28
|
+
// absent from the frame — the loudest failure in this seam and the one worth asking about first.
|
|
29
|
+
for (let i = 0; i < usage.nodeKinds.length; i++) {
|
|
30
|
+
const kind = usage.nodeKinds[i];
|
|
31
|
+
if (runtime.rendererMap.has(kind)) {
|
|
32
|
+
out?.push({ coverage: SceneCoverage.Satisfied, kind, registry: RenderRegistry.NodeRenderer });
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
found = true;
|
|
36
|
+
if (stopAtFirst)
|
|
37
|
+
return true;
|
|
38
|
+
out?.push({ coverage: SceneCoverage.Missing, kind, registry: RenderRegistry.NodeRenderer });
|
|
39
|
+
}
|
|
40
|
+
// Only meaningful for a state that rasterizes shapes. A GL or WebGPU state drawing every shape
|
|
41
|
+
// through the mesh path never replays a command, so these entries are reported and simply not acted
|
|
42
|
+
// on — the same shape as reporting 3D node kinds a backend collects structurally.
|
|
43
|
+
const commands = runtime.canvasShapeCommandRegistry;
|
|
44
|
+
for (let i = 0; i < usage.shapeCommandKeys.length; i++) {
|
|
45
|
+
const kind = usage.shapeCommandKeys[i];
|
|
46
|
+
if (commands?.has(kind) === true) {
|
|
47
|
+
out?.push({ coverage: SceneCoverage.Satisfied, kind, registry: RenderRegistry.ShapeCommandHandler });
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
found = true;
|
|
51
|
+
if (stopAtFirst)
|
|
52
|
+
return true;
|
|
53
|
+
out?.push({ coverage: SceneCoverage.Missing, kind, registry: RenderRegistry.ShapeCommandHandler });
|
|
54
|
+
}
|
|
55
|
+
return found;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=explainScene2DCoverage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"explainScene2DCoverage.js","sourceRoot":"","sources":["../src/explainScene2DCoverage.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAEtD,oGAAoG;AACpG,oGAAoG;AACpG,EAAE;AACF,uGAAuG;AACvG,uGAAuG;AACvG,sGAAsG;AACtG,iGAAiG;AACjG,EAAE;AACF,qGAAqG;AACrG,iGAAiG;AACjG,MAAM,UAAU,sBAAsB,CACpC,GAAyB,EACzB,KAAkB,EAClB,KAAiC;IAEjC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC;IACf,0BAA0B,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;AACvD,CAAC;AAED,mGAAmG;AACnG,mGAAmG;AACnG,MAAM,UAAU,kBAAkB,CAAC,KAAkB,EAAE,KAAiC;IACtF,OAAO,CAAC,0BAA0B,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;AAC/D,CAAC;AAED,qGAAqG;AACrG,0GAA0G;AAC1G,SAAS,0BAA0B,CACjC,GAAgC,EAChC,KAAkB,EAClB,KAAiC,EACjC,WAAoB;IAEpB,IAAI,KAAK,GAAG,KAAK,CAAC;IAClB,MAAM,OAAO,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC;IAE7C,+FAA+F;IAC/F,iGAAiG;IACjG,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAChD,MAAM,IAAI,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QAChC,IAAI,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YAClC,GAAG,EAAE,IAAI,CAAC,EAAE,QAAQ,EAAE,aAAa,CAAC,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,cAAc,CAAC,YAAY,EAAE,CAAC,CAAC;YAC9F,SAAS;QACX,CAAC;QACD,KAAK,GAAG,IAAI,CAAC;QACb,IAAI,WAAW;YAAE,OAAO,IAAI,CAAC;QAC7B,GAAG,EAAE,IAAI,CAAC,EAAE,QAAQ,EAAE,aAAa,CAAC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,cAAc,CAAC,YAAY,EAAE,CAAC,CAAC;IAC9F,CAAC;IAED,+FAA+F;IAC/F,oGAAoG;IACpG,kFAAkF;IAClF,MAAM,QAAQ,GAAG,OAAO,CAAC,0BAA0B,CAAC;IACpD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvD,MAAM,IAAI,GAAG,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC;QACvC,IAAI,QAAQ,EAAE,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;YACjC,GAAG,EAAE,IAAI,CAAC,EAAE,QAAQ,EAAE,aAAa,CAAC,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,cAAc,CAAC,mBAAmB,EAAE,CAAC,CAAC;YACrG,SAAS;QACX,CAAC;QACD,KAAK,GAAG,IAAI,CAAC;QACb,IAAI,WAAW;YAAE,OAAO,IAAI,CAAC;QAC7B,GAAG,EAAE,IAAI,CAAC,EAAE,QAAQ,EAAE,aAAa,CAAC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,cAAc,CAAC,mBAAmB,EAAE,CAAC,CAAC;IACrG,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -24,8 +24,17 @@ export function explainRenderRegistryMisses(state) {
|
|
|
24
24
|
}
|
|
25
25
|
function getRenderRegistryMissMessage(state, registry) {
|
|
26
26
|
switch (registry) {
|
|
27
|
+
// GL composites through an explicit per-mode realization; Canvas and DOM express blend modes
|
|
28
|
+
// natively and never report this.
|
|
29
|
+
case RenderRegistry.BlendRealization:
|
|
30
|
+
return 'a blend mode this scene uses has no registered GL realization, so the node composites as Normal instead — call registerGlBlendMode(state, blendMode, realization)';
|
|
27
31
|
case RenderRegistry.EffectPaddingResolver:
|
|
28
32
|
return 'computeRenderEffectPadding: effect kind has no registered padding resolver — call registerRenderEffectPaddingResolver(state, kind, resolver)';
|
|
33
|
+
// Reported by the resource layer, not the frame path: a material kind with no lister has its
|
|
34
|
+
// texture slots invisible to anything that walks materials. Discovery no longer depends on this
|
|
35
|
+
// (it reads the resource back-edge), so the surviving cost is a mesh whose reveal never fires.
|
|
36
|
+
case RenderRegistry.MaterialTextureLister:
|
|
37
|
+
return 'a material kind in this scene has no registered texture lister, so a mesh waiting on its maps never reveals — call registerScene3DMaterialTextures(registry, kind, lister), or the named door for that family';
|
|
29
38
|
// Reported only by the GPU backends, where an unresolved material means the node does not draw at
|
|
30
39
|
// all. The Canvas renderer treats a missing material renderer as "draw normally", so the same
|
|
31
40
|
// absence there is the ordinary case rather than a defect.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderRegistryGuards.js","sourceRoot":"","sources":["../src/renderRegistryGuards.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"renderRegistryGuards.js","sourceRoot":"","sources":["../src/renderRegistryGuards.ts"],"names":[],"mappings":"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"}
|
package/dist/renderer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../src/renderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAMtG,wBAAgB,+BAA+B,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI,CAG9F;AAED,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI,CAI3F;AAID,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../src/renderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAMtG,wBAAgB,+BAA+B,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI,CAG9F;AAED,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI,CAI3F;AAID,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI,CAY3F;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,GAAG,YAAY,GAAG,IAAI,CAE9F;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAKzF;AAKD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,aAAa,CAAC,SAAS,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,GAAG,IAAI,CAE7G"}
|
package/dist/renderer.js
CHANGED
|
@@ -15,9 +15,16 @@ export function copyRenderersFromRenderState(target, source) {
|
|
|
15
15
|
// cloned, never aliased: derived states start with a snapshot and diverge until an explicit re-copy.
|
|
16
16
|
export function copyRenderStateRegistrations(target, source) {
|
|
17
17
|
const targetRuntime = getRenderStateRuntime(target);
|
|
18
|
-
const
|
|
18
|
+
const sourceRuntime = getRenderStateRuntime(source);
|
|
19
|
+
const sourcePaddingRegistry = sourceRuntime.renderEffectPaddingResolverRegistry;
|
|
19
20
|
targetRuntime.renderEffectPaddingResolverRegistry =
|
|
20
|
-
|
|
21
|
+
sourcePaddingRegistry === null || sourcePaddingRegistry === undefined ? null : new Map(sourcePaddingRegistry);
|
|
22
|
+
// The shape-command set is a base-runtime registry every backend replays through, so a pipeline
|
|
23
|
+
// that inherits the renderers must inherit the commands too. Without it an offscreen state resolves
|
|
24
|
+
// no handler for any command in a shape's stream and bakes an empty target.
|
|
25
|
+
const sourceShapeCommands = sourceRuntime.canvasShapeCommandRegistry;
|
|
26
|
+
targetRuntime.canvasShapeCommandRegistry =
|
|
27
|
+
sourceShapeCommands === null || sourceShapeCommands === undefined ? null : new Map(sourceShapeCommands);
|
|
21
28
|
}
|
|
22
29
|
export function noopRendererData(_state, _source) {
|
|
23
30
|
return null;
|
package/dist/renderer.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderer.js","sourceRoot":"","sources":["../src/renderer.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAEtD,uGAAuG;AACvG,8FAA8F;AAC9F,MAAM,UAAU,+BAA+B,CAAC,MAAmB,EAAE,MAAmB;IACtF,4BAA4B,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7C,IAAI,MAAM,CAAC,sBAAsB,KAAK,IAAI;QAAE,MAAM,CAAC,sBAAsB,GAAG,MAAM,CAAC,sBAAsB,CAAC;AAC5G,CAAC;AAED,MAAM,UAAU,4BAA4B,CAAC,MAAmB,EAAE,MAAmB;IACnF,qBAAqB,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE;QACnE,gBAAgB,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;AACL,CAAC;AAED,kGAAkG;AAClG,qGAAqG;AACrG,MAAM,UAAU,4BAA4B,CAAC,MAAmB,EAAE,MAAmB;IACnF,MAAM,aAAa,GAAG,qBAAqB,CAAC,MAAM,CAAC,CAAC;IACpD,MAAM,
|
|
1
|
+
{"version":3,"file":"renderer.js","sourceRoot":"","sources":["../src/renderer.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAEtD,uGAAuG;AACvG,8FAA8F;AAC9F,MAAM,UAAU,+BAA+B,CAAC,MAAmB,EAAE,MAAmB;IACtF,4BAA4B,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7C,IAAI,MAAM,CAAC,sBAAsB,KAAK,IAAI;QAAE,MAAM,CAAC,sBAAsB,GAAG,MAAM,CAAC,sBAAsB,CAAC;AAC5G,CAAC;AAED,MAAM,UAAU,4BAA4B,CAAC,MAAmB,EAAE,MAAmB;IACnF,qBAAqB,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE;QACnE,gBAAgB,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;AACL,CAAC;AAED,kGAAkG;AAClG,qGAAqG;AACrG,MAAM,UAAU,4BAA4B,CAAC,MAAmB,EAAE,MAAmB;IACnF,MAAM,aAAa,GAAG,qBAAqB,CAAC,MAAM,CAAC,CAAC;IACpD,MAAM,aAAa,GAAG,qBAAqB,CAAC,MAAM,CAAC,CAAC;IACpD,MAAM,qBAAqB,GAAG,aAAa,CAAC,mCAAmC,CAAC;IAChF,aAAa,CAAC,mCAAmC;QAC/C,qBAAqB,KAAK,IAAI,IAAI,qBAAqB,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,qBAAqB,CAAC,CAAC;IAChH,gGAAgG;IAChG,oGAAoG;IACpG,4EAA4E;IAC5E,MAAM,mBAAmB,GAAG,aAAa,CAAC,0BAA0B,CAAC;IACrE,aAAa,CAAC,0BAA0B;QACtC,mBAAmB,KAAK,IAAI,IAAI,mBAAmB,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,mBAAmB,CAAC,CAAC;AAC5G,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,MAAmB,EAAE,OAAmB;IACvE,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAkB,EAAE,IAAU,EAAE,QAAkB;IACjF,MAAM,OAAO,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC;IAC7C,IAAI,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,QAAQ;QAAE,OAAO;IACvD,OAAO,CAAC,aAAa,GAAG,CAAC,OAAO,CAAC,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;IAC1D,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC1C,CAAC;AAED,oGAAoG;AACpG,qGAAqG;AACrG,kFAAkF;AAClF,MAAM,UAAU,iBAAiB,CAAC,KAAkB,EAAE,OAAiD;IACrG,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,OAAO;QAAE,gBAAgB,CAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;AAClF,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flighthq/render",
|
|
3
|
-
"version": "0.3.0-next.
|
|
3
|
+
"version": "0.3.0-next.1702.8973fda",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/flighthq/flight.git",
|
|
@@ -36,15 +36,15 @@
|
|
|
36
36
|
"clean:dist": "tsx ../../scripts/clean-package-dist.ts"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@flighthq/color": "0.3.0-next.
|
|
40
|
-
"@flighthq/entity": "0.3.0-next.
|
|
41
|
-
"@flighthq/geometry": "0.3.0-next.
|
|
42
|
-
"@flighthq/log": "0.3.0-next.
|
|
43
|
-
"@flighthq/materials": "0.3.0-next.
|
|
44
|
-
"@flighthq/mesh": "0.3.0-next.
|
|
45
|
-
"@flighthq/node": "0.3.0-next.
|
|
46
|
-
"@flighthq/signals": "0.3.0-next.
|
|
47
|
-
"@flighthq/types": "0.3.0-next.
|
|
39
|
+
"@flighthq/color": "0.3.0-next.1702.8973fda",
|
|
40
|
+
"@flighthq/entity": "0.3.0-next.1702.8973fda",
|
|
41
|
+
"@flighthq/geometry": "0.3.0-next.1702.8973fda",
|
|
42
|
+
"@flighthq/log": "0.3.0-next.1702.8973fda",
|
|
43
|
+
"@flighthq/materials": "0.3.0-next.1702.8973fda",
|
|
44
|
+
"@flighthq/mesh": "0.3.0-next.1702.8973fda",
|
|
45
|
+
"@flighthq/node": "0.3.0-next.1702.8973fda",
|
|
46
|
+
"@flighthq/signals": "0.3.0-next.1702.8973fda",
|
|
47
|
+
"@flighthq/types": "0.3.0-next.1702.8973fda"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@flighthq/adjustments": "*",
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { Renderer, Scene2DKindUsage, SceneCoverageEntry } from '@flighthq/types/contract';
|
|
2
|
+
import { RenderRegistry, SceneCoverage } from '@flighthq/types/contract';
|
|
3
|
+
import { describe, expect, it } from 'vitest';
|
|
4
|
+
|
|
5
|
+
import { explainScene2DCoverage, hasScene2DCoverage } from './explainScene2DCoverage';
|
|
6
|
+
import { registerRenderer } from './renderer';
|
|
7
|
+
import { createRenderState } from './renderState';
|
|
8
|
+
import { getRenderStateRuntime } from './renderState';
|
|
9
|
+
|
|
10
|
+
const renderer: Renderer = { createData: () => null, submit: () => {} } as unknown as Renderer;
|
|
11
|
+
|
|
12
|
+
function usage(overrides: Partial<Scene2DKindUsage> = {}): Scene2DKindUsage {
|
|
13
|
+
return { blendModes: [], materialKinds: [], nodeKinds: [], shapeCommandKeys: [], ...overrides };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function entries(state: ReturnType<typeof createRenderState>, u: Scene2DKindUsage): SceneCoverageEntry[] {
|
|
17
|
+
const out: SceneCoverageEntry[] = [];
|
|
18
|
+
explainScene2DCoverage(out, state, u);
|
|
19
|
+
return out;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// The registry is a plain Map on the base runtime; registering a real command means reaching through
|
|
23
|
+
// scene2d-canvas, which render cannot depend on, so the test installs the binding directly.
|
|
24
|
+
function wireShapeCommand(state: ReturnType<typeof createRenderState>, key: string): void {
|
|
25
|
+
const runtime = getRenderStateRuntime(state);
|
|
26
|
+
(runtime.canvasShapeCommandRegistry ??= new Map()).set(key, { key, draw: () => {} } as never);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
describe('explainScene2DCoverage', () => {
|
|
30
|
+
it('reports an unregistered node kind as Missing, since the node and its subtree never draw', () => {
|
|
31
|
+
expect(entries(createRenderState(), usage({ nodeKinds: ['Shape'] }))).toEqual([
|
|
32
|
+
{ coverage: SceneCoverage.Missing, kind: 'Shape', registry: RenderRegistry.NodeRenderer },
|
|
33
|
+
]);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('reports a registered node kind as Satisfied rather than omitting it', () => {
|
|
37
|
+
const state = createRenderState();
|
|
38
|
+
registerRenderer(state, 'Shape', renderer);
|
|
39
|
+
expect(entries(state, usage({ nodeKinds: ['Shape'] }))).toEqual([
|
|
40
|
+
{ coverage: SceneCoverage.Satisfied, kind: 'Shape', registry: RenderRegistry.NodeRenderer },
|
|
41
|
+
]);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('reports an unhandled shape command key against the state that would replay it', () => {
|
|
45
|
+
expect(entries(createRenderState(), usage({ shapeCommandKeys: ['beginFill'] }))).toContainEqual({
|
|
46
|
+
coverage: SceneCoverage.Missing,
|
|
47
|
+
kind: 'beginFill',
|
|
48
|
+
registry: RenderRegistry.ShapeCommandHandler,
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('reports a wired shape command key as Satisfied', () => {
|
|
53
|
+
const state = createRenderState();
|
|
54
|
+
wireShapeCommand(state, 'beginFill');
|
|
55
|
+
expect(entries(state, usage({ shapeCommandKeys: ['beginFill'] }))).toContainEqual({
|
|
56
|
+
coverage: SceneCoverage.Satisfied,
|
|
57
|
+
kind: 'beginFill',
|
|
58
|
+
registry: RenderRegistry.ShapeCommandHandler,
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('answers both registries in one manifest', () => {
|
|
63
|
+
const state = createRenderState();
|
|
64
|
+
registerRenderer(state, 'Shape', renderer);
|
|
65
|
+
const found = entries(state, usage({ nodeKinds: ['Shape', 'Sprite'], shapeCommandKeys: ['beginFill'] }));
|
|
66
|
+
expect(found).toHaveLength(3);
|
|
67
|
+
expect(found.filter((e) => e.coverage === SceneCoverage.Missing).map((e) => e.kind)).toEqual([
|
|
68
|
+
'Sprite',
|
|
69
|
+
'beginFill',
|
|
70
|
+
]);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('clears out, so a repeated call does not accumulate', () => {
|
|
74
|
+
const state = createRenderState();
|
|
75
|
+
const u = usage({ nodeKinds: ['Shape'] });
|
|
76
|
+
const out: SceneCoverageEntry[] = [];
|
|
77
|
+
explainScene2DCoverage(out, state, u);
|
|
78
|
+
const first = out.length;
|
|
79
|
+
explainScene2DCoverage(out, state, u);
|
|
80
|
+
expect(out).toHaveLength(first);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe('hasScene2DCoverage', () => {
|
|
85
|
+
it('is false while any node kind is unserved', () => {
|
|
86
|
+
expect(hasScene2DCoverage(createRenderState(), usage({ nodeKinds: ['Shape'] }))).toBe(false);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('stays true when the manifest is all Satisfied entries, which are not shortfalls', () => {
|
|
90
|
+
const state = createRenderState();
|
|
91
|
+
registerRenderer(state, 'Shape', renderer);
|
|
92
|
+
wireShapeCommand(state, 'beginFill');
|
|
93
|
+
const u = usage({ nodeKinds: ['Shape'], shapeCommandKeys: ['beginFill'] });
|
|
94
|
+
const out: SceneCoverageEntry[] = [];
|
|
95
|
+
explainScene2DCoverage(out, state, u);
|
|
96
|
+
expect(out.every((e) => e.coverage === SceneCoverage.Satisfied)).toBe(true);
|
|
97
|
+
expect(hasScene2DCoverage(state, u)).toBe(true);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('agrees with the explain tier, so the two can never disagree', () => {
|
|
101
|
+
const state = createRenderState();
|
|
102
|
+
registerRenderer(state, 'Shape', renderer);
|
|
103
|
+
const u = usage({ nodeKinds: ['Shape', 'Sprite'] });
|
|
104
|
+
const out: SceneCoverageEntry[] = [];
|
|
105
|
+
explainScene2DCoverage(out, state, u);
|
|
106
|
+
const gaps = out.filter((e) => e.coverage !== SceneCoverage.Satisfied);
|
|
107
|
+
expect(hasScene2DCoverage(state, u)).toBe(gaps.length === 0);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('is true for a scene that names no kinds at all', () => {
|
|
111
|
+
expect(hasScene2DCoverage(createRenderState(), usage())).toBe(true);
|
|
112
|
+
});
|
|
113
|
+
});
|
package/src/renderer.test.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Scene2DClipHooks, Renderer, RenderState } from '@flighthq/types/contract';
|
|
1
|
+
import type { CanvasShapeCommand, Scene2DClipHooks, Renderer, RenderState } from '@flighthq/types/contract';
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
copyAllRenderersFromRenderState,
|
|
@@ -83,6 +83,29 @@ describe('copyRenderStateRegistrations', () => {
|
|
|
83
83
|
);
|
|
84
84
|
expect(getRenderStateRuntime(target).renderEffectPaddingResolverRegistry?.get('acme.Effect')).toBe(resolver);
|
|
85
85
|
});
|
|
86
|
+
|
|
87
|
+
it('snapshot-copies the shape-command registry without aliasing the map', () => {
|
|
88
|
+
const source = createRenderState();
|
|
89
|
+
const target = createRenderState();
|
|
90
|
+
const command = { key: 'beginFill', draw: vi.fn() } as unknown as CanvasShapeCommand;
|
|
91
|
+
getRenderStateRuntime(source).canvasShapeCommandRegistry = new Map([['beginFill', command]]);
|
|
92
|
+
|
|
93
|
+
copyRenderStateRegistrations(target, source);
|
|
94
|
+
|
|
95
|
+
expect(getRenderStateRuntime(target).canvasShapeCommandRegistry).not.toBe(
|
|
96
|
+
getRenderStateRuntime(source).canvasShapeCommandRegistry,
|
|
97
|
+
);
|
|
98
|
+
expect(getRenderStateRuntime(target).canvasShapeCommandRegistry?.get('beginFill')).toBe(command);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('leaves the shape-command registry null when the source never registered one', () => {
|
|
102
|
+
const source = createRenderState();
|
|
103
|
+
const target = createRenderState();
|
|
104
|
+
|
|
105
|
+
copyRenderStateRegistrations(target, source);
|
|
106
|
+
|
|
107
|
+
expect(getRenderStateRuntime(target).canvasShapeCommandRegistry).toBeNull();
|
|
108
|
+
});
|
|
86
109
|
});
|
|
87
110
|
|
|
88
111
|
describe('noopRendererData', () => {
|