@forgeax/engine-app 0.1.7 → 0.1.20
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/.tsbuildinfo +1 -1
- package/dist/__tests__/browser-frame-signal.browser.test.d.ts +2 -0
- package/dist/__tests__/browser-frame-signal.browser.test.d.ts.map +1 -0
- package/dist/__tests__/browser-frame-signal.unit.test.d.ts +2 -0
- package/dist/__tests__/browser-frame-signal.unit.test.d.ts.map +1 -0
- package/dist/__tests__/tool-preview-runtime-contract.unit.test.d.ts +2 -0
- package/dist/__tests__/tool-preview-runtime-contract.unit.test.d.ts.map +1 -0
- package/dist/canvas-policy.d.ts.map +1 -1
- package/dist/create-app.d.ts.map +1 -1
- package/dist/engine-worker-runtime.mjs +1 -0
- package/dist/engine-worker-runtime.mjs.map +1 -1
- package/dist/index.mjs +75 -28
- package/dist/index.mjs.map +1 -1
- package/dist/internal/browser-rhi-debug-runtime.d.ts +2 -2
- package/dist/internal/browser-rhi-debug-runtime.d.ts.map +1 -1
- package/dist/tool-preview/bootstrap.d.ts.map +1 -1
- package/package.json +25 -25
- package/src/__tests__/browser-frame-signal.browser.test.ts +20 -0
- package/src/__tests__/browser-frame-signal.unit.test.ts +35 -0
- package/src/__tests__/create-app-stop.test.ts +10 -6
- package/src/__tests__/create-app.test.ts +13 -1
- package/src/__tests__/tool-preview-runtime-contract.unit.test.ts +47 -0
- package/src/browser-frame-signal.ts +4 -4
- package/src/canvas-policy.ts +1 -0
- package/src/create-app.ts +1 -0
- package/src/internal/browser-rhi-debug-runtime.ts +9 -6
- package/src/tool-preview/bootstrap.ts +55 -13
package/dist/index.mjs
CHANGED
|
@@ -3,7 +3,7 @@ export { Context } from '@forgeax/engine-plugin';
|
|
|
3
3
|
export { createFullscreenRenderFeature } from '@forgeax/engine-render/authoring';
|
|
4
4
|
import { Camera, perspective, renderComponentsPlugin, CAMERA_PROJECTION_PERSPECTIVE } from '@forgeax/engine-render';
|
|
5
5
|
import { RhiError } from '@forgeax/engine-rhi';
|
|
6
|
-
import { attachRecorder, decodeTape, openReplay, decodeToRgba8, buildTapeIndex, createRhiDebugError } from '@forgeax/engine-rhi-debug';
|
|
6
|
+
import { attachRecorder, decodeTape, openReplay, decodeToRgba8, buildTapeIndex, replayDeviceRequest, createRhiDebugError } from '@forgeax/engine-rhi-debug';
|
|
7
7
|
import * as engineRuntimeModule from '@forgeax/engine-runtime';
|
|
8
8
|
import { EngineEnvironmentError } from '@forgeax/engine-runtime';
|
|
9
9
|
import { loadRhiPack, constructRuntimeRendererHost } from '@forgeax/engine-runtime/internal/renderer-host';
|
|
@@ -353,8 +353,8 @@ var APP_EXPECTED = Object.fromEntries(
|
|
|
353
353
|
var APP_ERROR_HINTS = Object.fromEntries(
|
|
354
354
|
Object.entries(appErrorPolicy).map(([code, policy]) => [code, policy.hint])
|
|
355
355
|
);
|
|
356
|
-
function isAppError(
|
|
357
|
-
return
|
|
356
|
+
function isAppError(err14) {
|
|
357
|
+
return err14 instanceof AppErrorClass;
|
|
358
358
|
}
|
|
359
359
|
function executionBootstrapHostPlugin(host) {
|
|
360
360
|
return {
|
|
@@ -832,16 +832,16 @@ var ErrorFanoutRegistry = class {
|
|
|
832
832
|
* console.error(err) so the unhandled error is visible in devtools
|
|
833
833
|
* (charter P3 explicit failure: silent drop is a footgun).
|
|
834
834
|
*/
|
|
835
|
-
fire(
|
|
835
|
+
fire(err14) {
|
|
836
836
|
if (this.listeners.size === 0) {
|
|
837
837
|
if (!this.silenceUnhandledErrors) {
|
|
838
|
-
console.error(
|
|
838
|
+
console.error(err14);
|
|
839
839
|
}
|
|
840
840
|
return;
|
|
841
841
|
}
|
|
842
842
|
const snapshot = Array.from(this.listeners);
|
|
843
843
|
for (const cb of snapshot) {
|
|
844
|
-
cb(
|
|
844
|
+
cb(err14);
|
|
845
845
|
}
|
|
846
846
|
}
|
|
847
847
|
/**
|
|
@@ -2023,13 +2023,13 @@ function attachInputAuto(canvas, options = {}) {
|
|
|
2023
2023
|
...options.lockProvider ? { lockProvider: options.lockProvider } : {},
|
|
2024
2024
|
onLockError: (detail) => {
|
|
2025
2025
|
if (onLockErrorDispatch) {
|
|
2026
|
-
const
|
|
2026
|
+
const err14 = new AppError({
|
|
2027
2027
|
code: "app-pointer-lock-failed",
|
|
2028
2028
|
expected: APP_EXPECTED["app-pointer-lock-failed"],
|
|
2029
2029
|
hint: APP_ERROR_HINTS["app-pointer-lock-failed"],
|
|
2030
2030
|
detail
|
|
2031
2031
|
});
|
|
2032
|
-
onLockErrorDispatch(
|
|
2032
|
+
onLockErrorDispatch(err14);
|
|
2033
2033
|
}
|
|
2034
2034
|
}
|
|
2035
2035
|
};
|
|
@@ -2088,21 +2088,27 @@ function inputPlugin() {
|
|
|
2088
2088
|
}
|
|
2089
2089
|
};
|
|
2090
2090
|
}
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2091
|
+
function selectSwapChainFormat(storageBufferCapable, surfaceViewFormats = true) {
|
|
2092
|
+
if (!storageBufferCapable) {
|
|
2093
|
+
return {
|
|
2094
|
+
storage: "rgba8unorm",
|
|
2095
|
+
view: surfaceViewFormats ? "rgba8unorm-srgb" : "rgba8unorm"
|
|
2096
|
+
};
|
|
2097
|
+
}
|
|
2095
2098
|
const nav = globalThis.navigator;
|
|
2096
2099
|
const gpu = nav?.gpu;
|
|
2097
2100
|
const getPreferred = gpu?.getPreferredCanvasFormat;
|
|
2098
2101
|
if (gpu !== void 0 && typeof getPreferred === "function") {
|
|
2099
2102
|
const storage = getPreferred.call(gpu);
|
|
2100
2103
|
const view = storage === "rgba8unorm" ? "rgba8unorm-srgb" : storage === "bgra8unorm" ? "bgra8unorm-srgb" : storage;
|
|
2101
|
-
return {
|
|
2104
|
+
return {
|
|
2105
|
+
storage,
|
|
2106
|
+
view: surfaceViewFormats ? view : storage
|
|
2107
|
+
};
|
|
2102
2108
|
}
|
|
2103
2109
|
return {
|
|
2104
2110
|
storage: "rgba8unorm",
|
|
2105
|
-
view: "rgba8unorm-srgb",
|
|
2111
|
+
view: surfaceViewFormats ? "rgba8unorm-srgb" : "rgba8unorm",
|
|
2106
2112
|
fallbackReason: "preferred-canvas-format-missing"
|
|
2107
2113
|
};
|
|
2108
2114
|
}
|
|
@@ -2760,6 +2766,7 @@ function syncCameraAspect(world, canvasW, canvasH) {
|
|
|
2760
2766
|
if (!r.ok) continue;
|
|
2761
2767
|
if (r.value.autoAspect !== true) continue;
|
|
2762
2768
|
if (r.value.projection !== CAMERA_PROJECTION_PERSPECTIVE) continue;
|
|
2769
|
+
if (r.value.aspect === Math.fround(aspect)) continue;
|
|
2763
2770
|
world.set(entity, Camera, { aspect });
|
|
2764
2771
|
}
|
|
2765
2772
|
}
|
|
@@ -3142,8 +3149,8 @@ var LOAD_GAME_EXPECTED = Object.fromEntries(
|
|
|
3142
3149
|
var LOAD_GAME_ERROR_HINTS = Object.fromEntries(
|
|
3143
3150
|
Object.entries(loadGameErrorPolicy).map(([code, policy]) => [code, policy.hint])
|
|
3144
3151
|
);
|
|
3145
|
-
function isLoadGameError(
|
|
3146
|
-
return
|
|
3152
|
+
function isLoadGameError(err14) {
|
|
3153
|
+
return err14 instanceof LoadGameErrorClass;
|
|
3147
3154
|
}
|
|
3148
3155
|
function isPlugin(value) {
|
|
3149
3156
|
return typeof value === "function" || typeof value === "object" && value !== null && "apply" in value && typeof value.apply === "function";
|
|
@@ -3204,10 +3211,12 @@ async function createBrowserRhiDebugRuntime() {
|
|
|
3204
3211
|
attachment,
|
|
3205
3212
|
createShaderModule: backend.createShaderModule,
|
|
3206
3213
|
rhiInstrumentation: createRhiInstrumentation(attachment),
|
|
3207
|
-
createReplayDevice: async () => {
|
|
3214
|
+
createReplayDevice: async (tape) => {
|
|
3208
3215
|
const adapter = await backend.rhi.requestAdapter();
|
|
3209
3216
|
if (!adapter.ok) return adapter;
|
|
3210
|
-
return adapter.value.requestDevice(
|
|
3217
|
+
return adapter.value.requestDevice(
|
|
3218
|
+
replayDeviceRequest(tape, adapter.value.features, adapter.value.limits)
|
|
3219
|
+
);
|
|
3211
3220
|
},
|
|
3212
3221
|
attachRenderer(_renderer) {
|
|
3213
3222
|
return () => {
|
|
@@ -3345,6 +3354,11 @@ function resourceSubjectDigest(resource) {
|
|
|
3345
3354
|
];
|
|
3346
3355
|
return candidates.find((value) => typeof value === "string" && value.length > 0);
|
|
3347
3356
|
}
|
|
3357
|
+
function resourceSubjectDrawn(resource, drawCalls) {
|
|
3358
|
+
if (resource.kind === "texture") return drawCalls > 0;
|
|
3359
|
+
if (resource.kind === "material" || resource.kind === "mesh") return drawCalls > 2;
|
|
3360
|
+
return resource.observation !== void 0;
|
|
3361
|
+
}
|
|
3348
3362
|
async function replayToolPreviewCapture(capture) {
|
|
3349
3363
|
const analyzeStartedAtMs = performance.now();
|
|
3350
3364
|
const tapeBytes = dataUriBytes(capture.tape.jsonUri);
|
|
@@ -3357,13 +3371,23 @@ async function replayToolPreviewCapture(capture) {
|
|
|
3357
3371
|
detail: { phase: "tape-parse", cause: parsed.error }
|
|
3358
3372
|
});
|
|
3359
3373
|
}
|
|
3360
|
-
const
|
|
3361
|
-
|
|
3374
|
+
const drawCalls = countDrawCalls(parsed.value);
|
|
3375
|
+
const resource = capture.resource !== void 0 && capture.resource.observation === void 0 && capture.resource.ownerFacts !== void 0 && resourceSubjectDrawn(capture.resource, drawCalls) ? { ...capture.resource, observation: capture.resource.ownerFacts } : capture.resource;
|
|
3376
|
+
const subjectDigest = resourceSubjectDigest(resource);
|
|
3377
|
+
if (resource !== void 0 && subjectDigest === void 0) {
|
|
3362
3378
|
return err({
|
|
3363
3379
|
code: "tool-preview-bootstrap-failed",
|
|
3364
3380
|
expected: "the resource owner to publish a subject identity before evidence publication",
|
|
3365
3381
|
hint: "repair AssetRegistry owner facts or post-frame observation; recipe and trace are not subject identity",
|
|
3366
|
-
detail: {
|
|
3382
|
+
detail: {
|
|
3383
|
+
phase: "resource-identity",
|
|
3384
|
+
cause: {
|
|
3385
|
+
kind: resource.kind,
|
|
3386
|
+
drawCalls,
|
|
3387
|
+
ownerFactsPublished: resource.ownerFacts !== void 0,
|
|
3388
|
+
observationPublished: resource.observation !== void 0
|
|
3389
|
+
}
|
|
3390
|
+
}
|
|
3367
3391
|
});
|
|
3368
3392
|
}
|
|
3369
3393
|
let runtime;
|
|
@@ -3377,7 +3401,7 @@ async function replayToolPreviewCapture(capture) {
|
|
|
3377
3401
|
detail: { phase: "replay-runtime", cause }
|
|
3378
3402
|
});
|
|
3379
3403
|
}
|
|
3380
|
-
const replayDevice = await runtime.createReplayDevice();
|
|
3404
|
+
const replayDevice = await runtime.createReplayDevice(parsed.value);
|
|
3381
3405
|
if (!replayDevice.ok) {
|
|
3382
3406
|
return err({
|
|
3383
3407
|
code: "tool-preview-capability-unavailable",
|
|
@@ -3399,7 +3423,6 @@ async function replayToolPreviewCapture(capture) {
|
|
|
3399
3423
|
});
|
|
3400
3424
|
}
|
|
3401
3425
|
const replay = replayResult.value;
|
|
3402
|
-
const drawCalls = countDrawCalls(parsed.value);
|
|
3403
3426
|
if (drawCalls === 0) {
|
|
3404
3427
|
await replay.dispose();
|
|
3405
3428
|
return err({
|
|
@@ -3619,7 +3642,7 @@ async function replayToolPreviewCapture(capture) {
|
|
|
3619
3642
|
}
|
|
3620
3643
|
}
|
|
3621
3644
|
},
|
|
3622
|
-
...
|
|
3645
|
+
...resource === void 0 ? {} : { resource }
|
|
3623
3646
|
});
|
|
3624
3647
|
}
|
|
3625
3648
|
async function createToolPreviewHost(options) {
|
|
@@ -3772,7 +3795,7 @@ async function createToolPreviewHost(options) {
|
|
|
3772
3795
|
});
|
|
3773
3796
|
const executeStartedAtMs = performance.now();
|
|
3774
3797
|
const actionTrace = [];
|
|
3775
|
-
|
|
3798
|
+
let encodedCapture;
|
|
3776
3799
|
for (let frame = 0; frame < recipe.frames; frame += 1) {
|
|
3777
3800
|
for (const action of recipe.actions.filter((candidate) => candidate.frame === frame)) {
|
|
3778
3801
|
let accepted = false;
|
|
@@ -3796,6 +3819,17 @@ async function createToolPreviewHost(options) {
|
|
|
3796
3819
|
}
|
|
3797
3820
|
actionTrace.push(action);
|
|
3798
3821
|
}
|
|
3822
|
+
if (frame === recipe.frames - 1) {
|
|
3823
|
+
encodedCapture = runtime.attachment.captureFrame();
|
|
3824
|
+
const snapshot = await runtime.attachment.frameBoundary();
|
|
3825
|
+
if (!snapshot.ok)
|
|
3826
|
+
return err({
|
|
3827
|
+
code: "tool-preview-bootstrap-failed",
|
|
3828
|
+
expected: "RHI-debug snapshots live resources before the captured frame",
|
|
3829
|
+
hint: snapshot.error.hint,
|
|
3830
|
+
detail: { phase: "rhi-snapshot", cause: snapshot.error }
|
|
3831
|
+
});
|
|
3832
|
+
}
|
|
3799
3833
|
const stepped = app.stepFrame(recipe.deltaSeconds);
|
|
3800
3834
|
if (!stepped.ok)
|
|
3801
3835
|
return err({
|
|
@@ -3805,9 +3839,14 @@ async function createToolPreviewHost(options) {
|
|
|
3805
3839
|
});
|
|
3806
3840
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
3807
3841
|
}
|
|
3808
|
-
await runtime.attachment.frameBoundary();
|
|
3809
|
-
|
|
3810
|
-
|
|
3842
|
+
const finalized = await runtime.attachment.frameBoundary();
|
|
3843
|
+
if (!finalized.ok)
|
|
3844
|
+
return err({
|
|
3845
|
+
code: "tool-preview-bootstrap-failed",
|
|
3846
|
+
expected: "RHI-debug finalizes the captured frame",
|
|
3847
|
+
hint: finalized.error.hint,
|
|
3848
|
+
detail: { phase: "rhi-finalize", cause: finalized.error }
|
|
3849
|
+
});
|
|
3811
3850
|
if (options.collectResourceFacts !== void 0) {
|
|
3812
3851
|
try {
|
|
3813
3852
|
const collected = await options.collectResourceFacts(app, resourceFacts);
|
|
@@ -3836,6 +3875,14 @@ async function createToolPreviewHost(options) {
|
|
|
3836
3875
|
detail: { phase: "profiler-finish", cause: profile.error }
|
|
3837
3876
|
});
|
|
3838
3877
|
}
|
|
3878
|
+
if (encodedCapture === void 0) {
|
|
3879
|
+
return err({
|
|
3880
|
+
code: "tool-preview-bootstrap-failed",
|
|
3881
|
+
expected: "a positive-frame recipe to arm one captured frame",
|
|
3882
|
+
hint: "validate the recipe before running the preview host",
|
|
3883
|
+
detail: { phase: "rhi-capture" }
|
|
3884
|
+
});
|
|
3885
|
+
}
|
|
3839
3886
|
const tape = await encodedCapture;
|
|
3840
3887
|
if (!tape.ok)
|
|
3841
3888
|
return err({
|