@volter/editor-game 0.5.65
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/NOTICE +23 -0
- package/contributions/asset-budget-asset.menu.ts +26 -0
- package/contributions/asset-budget.action.ts +18 -0
- package/contributions/asset-budget.document.tsx +21 -0
- package/contributions/asset-budget.menu.ts +22 -0
- package/contributions/audio-unlock.service.ts +17 -0
- package/contributions/audio.utility.tsx +15 -0
- package/contributions/autoplay.service.ts +48 -0
- package/contributions/bridge.command.ts +172 -0
- package/contributions/build-profiles.document.tsx +21 -0
- package/contributions/build-progress.status.tsx +45 -0
- package/contributions/build.action.ts +26 -0
- package/contributions/build.command.ts +27 -0
- package/contributions/build.header.tsx +37 -0
- package/contributions/build.menu.ts +31 -0
- package/contributions/build.service.ts +31 -0
- package/contributions/connection.status.tsx +43 -0
- package/contributions/coverage.service.ts +118 -0
- package/contributions/edit-mode-audio.service.ts +23 -0
- package/contributions/edit-mode-networking.service.ts +30 -0
- package/contributions/game-document.service.ts +25 -0
- package/contributions/game-eval.command.ts +210 -0
- package/contributions/game.layout.ts +19 -0
- package/contributions/gameplay.command.ts +255 -0
- package/contributions/generation.service.ts +96 -0
- package/contributions/generations.status.tsx +53 -0
- package/contributions/ingest.service.ts +80 -0
- package/contributions/instances.command.ts +75 -0
- package/contributions/navmesh.menu.ts +39 -0
- package/contributions/navmesh.service.ts +19 -0
- package/contributions/network.utility.tsx +17 -0
- package/contributions/play.command.ts +355 -0
- package/contributions/profiler.action.ts +16 -0
- package/contributions/profiler.menu.ts +22 -0
- package/contributions/profiler.utility.tsx +17 -0
- package/contributions/react/component-board.service.ts +21 -0
- package/contributions/react/design-time-mount.service.ts +60 -0
- package/contributions/react/pasteboard.action.ts +41 -0
- package/contributions/react/react-inspector.service.ts +85 -0
- package/contributions/react/story-documents.service.ts +44 -0
- package/contributions/scene-document.service.ts +32 -0
- package/contributions/state-watch.action.ts +17 -0
- package/contributions/state-watch.menu.ts +23 -0
- package/contributions/state-watch.utility.tsx +20 -0
- package/contributions/team-playtest.service.ts +124 -0
- package/contributions/three/camera-runtime.inspector.tsx +34 -0
- package/contributions/three/component-board.service.ts +24 -0
- package/contributions/three/component-verbs.command.ts +110 -0
- package/contributions/three/component-verbs.service.ts +92 -0
- package/contributions/three/constraints.inspector.tsx +34 -0
- package/contributions/three/model-asset-sections.service.ts +26 -0
- package/contributions/three/reflection-probe-capture.inspector.tsx +32 -0
- package/contributions/three/story-documents.service.ts +31 -0
- package/contributions/three/three-authoring.service.ts +66 -0
- package/contributions/transport.header.tsx +19 -0
- package/contributions/xstate-behavior.action.ts +42 -0
- package/contributions/xstate-behavior.document.tsx +73 -0
- package/contributions/xstate-behavior.inspector.tsx +28 -0
- package/contributions/xstate-behavior.menu.ts +23 -0
- package/package.json +144 -0
- package/src/asset-budget/AssetBudgetPanel.tsx +1172 -0
- package/src/asset-budget/asset-budget-model.ts +799 -0
- package/src/asset-budget/basis-encoder.ts +165 -0
- package/src/asset-budget/gltf-io.ts +154 -0
- package/src/asset-budget/gltf-optimize.ts +384 -0
- package/src/asset-budget/image-dims.ts +78 -0
- package/src/asset-budget/optimize-apply.ts +221 -0
- package/src/audio/AudioDebuggerPanel.tsx +457 -0
- package/src/audio/audio-debugger-model.ts +87 -0
- package/src/bridge/call.ts +60 -0
- package/src/bridge/dispatch.ts +459 -0
- package/src/bridge/live-frames.ts +25 -0
- package/src/bridge/screenshot.ts +316 -0
- package/src/build/BuildProfilesPanel.tsx +476 -0
- package/src/build/build-session.ts +247 -0
- package/src/build/format-bytes.ts +14 -0
- package/src/command-results.ts +36 -0
- package/src/coverage/live-authoring-surface.ts +30 -0
- package/src/coverage/live-project-verbs.ts +162 -0
- package/src/coverage/native-system-coverage.ts +143 -0
- package/src/coverage/root-coverage.ts +79 -0
- package/src/coverage/session-coverage.ts +193 -0
- package/src/design-system-stories/ApplicationChrome.stories.tsx +100 -0
- package/src/design-system-stories/InspectorNarrowBodies.stories.tsx +406 -0
- package/src/edit-mode/edit-mode-audio.ts +56 -0
- package/src/edit-mode/edit-mode-networking.ts +112 -0
- package/src/game-document/DevicePresetPicker.tsx +84 -0
- package/src/game-document/GameCaptureFrameButton.tsx +45 -0
- package/src/game-document/GameDocument.tsx +215 -0
- package/src/game-document/GamePanel.tsx +662 -0
- package/src/game-document/InstanceInspectorPicker.tsx +183 -0
- package/src/game-document/crowd-debug.ts +183 -0
- package/src/game-document/device-preview.ts +336 -0
- package/src/game-document/game-view-store.ts +107 -0
- package/src/game-document/physics-debug.ts +187 -0
- package/src/generation/GenerationActivity.tsx +421 -0
- package/src/generation/GenerationGallery.css +231 -0
- package/src/generation/generation-documents.tsx +338 -0
- package/src/generation/generation-jobs.ts +128 -0
- package/src/generation/generation-presentation.ts +257 -0
- package/src/host/adapter-reach.ts +445 -0
- package/src/host/adapter-runtime-bindings.ts +303 -0
- package/src/host/after-paint.ts +112 -0
- package/src/host/api/configurations.ts +66 -0
- package/src/host/authoring/babylon-authoring-adapter.ts +703 -0
- package/src/host/authoring/canvas-runtime-recognition.ts +50 -0
- package/src/host/authoring/contract-hierarchy-authoring.ts +144 -0
- package/src/host/authoring/contract-scenes-stories.ts +204 -0
- package/src/host/authoring/creation-site-related.ts +55 -0
- package/src/host/authoring/ephemeral-persistence.ts +29 -0
- package/src/host/authoring/gesture-persist.ts +84 -0
- package/src/host/authoring/ingest-data-writer.ts +232 -0
- package/src/host/authoring/ingest-source-persistence.ts +826 -0
- package/src/host/authoring/mount-isolated-pixi-screen.ts +250 -0
- package/src/host/authoring/mounted-authoring.ts +41 -0
- package/src/host/authoring/owned-pixi-ticker-listeners.ts +96 -0
- package/src/host/authoring/phaser-live-authoring-adapter.ts +265 -0
- package/src/host/authoring/pixi-authoring-adapter.ts +1554 -0
- package/src/host/authoring/pixi-creation-site-write-target.ts +60 -0
- package/src/host/authoring/pixi-isolation-assets.ts +25 -0
- package/src/host/authoring/pixi-live-write-target.ts +979 -0
- package/src/host/authoring/pixi-source-identity.ts +141 -0
- package/src/host/authoring/pixi-still-presentation.ts +71 -0
- package/src/host/authoring/pixi-structure-history.ts +237 -0
- package/src/host/authoring/pixi-transform-channels.ts +205 -0
- package/src/host/authoring/selection-remount-handoff.ts +23 -0
- package/src/host/authoring/source-persistence-backend.ts +373 -0
- package/src/host/authoring/source-refresh-revisions.ts +81 -0
- package/src/host/authoring/struct-write-pipe.ts +143 -0
- package/src/host/auto-frame-window.ts +89 -0
- package/src/host/binding-resolver.ts +393 -0
- package/src/host/browser-transpile.ts +631 -0
- package/src/host/canvas-entry-runtime.ts +95 -0
- package/src/host/components/CameraAuthoringOverlay.tsx +216 -0
- package/src/host/components/HeaderTelemetry.tsx +341 -0
- package/src/host/components/PixiIsolationSceneContent.tsx +280 -0
- package/src/host/components/ResolutionPicker.tsx +89 -0
- package/src/host/components/ThreeIsolationSceneContent.tsx +180 -0
- package/src/host/components/frame-debugger-model.ts +579 -0
- package/src/host/components/header-telemetry-model.ts +74 -0
- package/src/host/components/scene-document.tsx +447 -0
- package/src/host/components/utility-view-state.ts +87 -0
- package/src/host/components/world-root-stage-binding.tsx +133 -0
- package/src/host/components/world-root-stage.ts +1046 -0
- package/src/host/coverage/authoring-read-probe.ts +583 -0
- package/src/host/coverage/capability-coverage.ts +1587 -0
- package/src/host/coverage/coverage-accounting.ts +261 -0
- package/src/host/coverage/game-contract-seam-evidence.ts +82 -0
- package/src/host/coverage/project-verb-coverage.ts +148 -0
- package/src/host/coverage/system-adapter-coverage.ts +373 -0
- package/src/host/design-system-stories/StoryLayout.tsx +104 -0
- package/src/host/design-system-stories/fixtures/authoring.ts +247 -0
- package/src/host/design-system-stories/fixtures/editor-runtime.tsx +115 -0
- package/src/host/document-preview-three.ts +110 -0
- package/src/host/entry-adjudication.ts +89 -0
- package/src/host/game-location-guard.ts +150 -0
- package/src/host/game-module-access.ts +196 -0
- package/src/host/game-realm-page.ts +358 -0
- package/src/host/game-realm-reclaim.ts +55 -0
- package/src/host/game-realm-storage.ts +104 -0
- package/src/host/gameplay-export.ts +288 -0
- package/src/host/gameplay-recording.ts +717 -0
- package/src/host/gated-globals.ts +1511 -0
- package/src/host/history/json-history-resource.ts +254 -0
- package/src/host/ingest/registry.ts +261 -0
- package/src/host/instance-extract-actions.ts +120 -0
- package/src/host/instance-fork-actions.ts +120 -0
- package/src/host/play-control-hook.ts +26 -0
- package/src/host/playwright-shim.ts +479 -0
- package/src/host/projection/dom.ts +295 -0
- package/src/host/projection/pixi.ts +288 -0
- package/src/host/r3f-entry-runtime.ts +77 -0
- package/src/host/react-mount-runtime.ts +162 -0
- package/src/host/realm-services.ts +148 -0
- package/src/host/recording-preview.ts +106 -0
- package/src/host/roots/module-root.ts +203 -0
- package/src/host/roots/react-root.ts +181 -0
- package/src/host/same-realm-loop-gate.ts +544 -0
- package/src/host/scene-view-drawability.ts +69 -0
- package/src/host/sdk/tools.ts +31 -0
- package/src/host/served-bundle-runtime-modules.ts +331 -0
- package/src/host/server-log-bridge.ts +60 -0
- package/src/host/staged-projects.ts +24 -0
- package/src/host/stories/mounted-story-viewport-source.ts +64 -0
- package/src/host/stories/story-arg-descriptors.ts +50 -0
- package/src/host/stories/story-media-presence.ts +152 -0
- package/src/host/surface-content.ts +87 -0
- package/src/host/take-named-export.ts +23 -0
- package/src/host/three-ingest-runtime.ts +76 -0
- package/src/host/types-fastnoise-lite.d.ts +7 -0
- package/src/host/types-mikktspace.d.ts +20 -0
- package/src/host/types-troika-three-text.d.ts +7 -0
- package/src/host/use-active-performance-source.ts +53 -0
- package/src/host/viewport-pose-memory.ts +48 -0
- package/src/host/viewport-root-presentation.ts +40 -0
- package/src/ingest/active-ingest.ts +251 -0
- package/src/ingest/active-scene-navigation.ts +43 -0
- package/src/ingest/authoring/ingest-dom-surface-authoring.ts +191 -0
- package/src/ingest/authoring/ingest-root-adapter.ts +897 -0
- package/src/ingest/capture-wait-report.ts +122 -0
- package/src/ingest/deferred-ingest-play.ts +216 -0
- package/src/ingest/deferred-ingest-session.ts +23 -0
- package/src/ingest/discovery-public-ingest.ts +242 -0
- package/src/ingest/dom-stub-mark.ts +7 -0
- package/src/ingest/entry-load.ts +56 -0
- package/src/ingest/game-contract-realm.ts +34 -0
- package/src/ingest/game-pointer-lock.ts +89 -0
- package/src/ingest/held-scene-repaint.ts +73 -0
- package/src/ingest/host-surface-box.ts +174 -0
- package/src/ingest/ingest-boot-viewport.ts +54 -0
- package/src/ingest/ingest-canvas-scene-document.tsx +177 -0
- package/src/ingest/ingest-canvas-scene.ts +50 -0
- package/src/ingest/ingest-evidence-hook.ts +131 -0
- package/src/ingest/ingest-frame-snapshot.ts +183 -0
- package/src/ingest/ingest-play-commands.ts +134 -0
- package/src/ingest/ingest-play-control.ts +299 -0
- package/src/ingest/ingest-render-debug.ts +320 -0
- package/src/ingest/ingest-siblings.ts +487 -0
- package/src/ingest/ingest-status.ts +62 -0
- package/src/ingest/live-ingest-facet.ts +46 -0
- package/src/ingest/module-mode.ts +229 -0
- package/src/ingest/mount-canvas-ingest-root.ts +899 -0
- package/src/ingest/mount-coverage.ts +296 -0
- package/src/ingest/mount-dom-ingest-root.ts +282 -0
- package/src/ingest/mount-ingest-root.ts +744 -0
- package/src/ingest/mount-three-ingest-root.ts +366 -0
- package/src/ingest/resolve-canvas.ts +47 -0
- package/src/ingest/resolve-three.ts +123 -0
- package/src/ingest/served-bundle.ts +109 -0
- package/src/ingest/served-html-boot.ts +292 -0
- package/src/ingest/surface-canvas.ts +84 -0
- package/src/ingest/surface-dom.ts +84 -0
- package/src/ingest/surface-three.ts +128 -0
- package/src/ingest/types.ts +76 -0
- package/src/ingest/unmount-ingest-root.ts +224 -0
- package/src/navmesh/navmesh-actions.ts +27 -0
- package/src/navmesh/navmesh-handler.ts +237 -0
- package/src/navmesh/navmesh-workflow-store.ts +78 -0
- package/src/network/NetworkInspectorPanel.tsx +644 -0
- package/src/network/network-inspector-model.ts +225 -0
- package/src/play/play-boot-stall.ts +118 -0
- package/src/play/play-log-events.ts +26 -0
- package/src/play/play-mode.ts +2553 -0
- package/src/play/play-recording.ts +335 -0
- package/src/play/react-play-live-authoring.ts +165 -0
- package/src/play-bar/PlayBar.tsx +488 -0
- package/src/play-bar/PlayerCountPicker.tsx +100 -0
- package/src/profiler/FrameDebuggerPanel.tsx +458 -0
- package/src/profiler/PerformancePanel.tsx +1068 -0
- package/src/profiler/ProfilerPanel.tsx +53 -0
- package/src/profiler/frame-debugger-store.ts +97 -0
- package/src/profiler/main-thread-busy.ts +90 -0
- package/src/react/design-time-react-mount.ts +782 -0
- package/src/react/dom-authoring-adapter.ts +764 -0
- package/src/react/pasteboard-materialize.ts +154 -0
- package/src/react/react-inspector-section.tsx +2496 -0
- package/src/react/react-world-authoring-adapter.ts +3795 -0
- package/src/react/story-documents/story-args-section.ts +19 -0
- package/src/react/story-documents/story-documents.tsx +1380 -0
- package/src/react/story-paint-bounds.ts +94 -0
- package/src/react/ui-board-document.tsx +101 -0
- package/src/react/ui-board-title.ts +9 -0
- package/src/react/ui-component-board.ts +72 -0
- package/src/services/audio-pose-guard.ts +81 -0
- package/src/services/game-audio-unlock.ts +48 -0
- package/src/state-watch/StateWatchPanel.tsx +535 -0
- package/src/three/authoring/camera-runtime-inspector-section.tsx +135 -0
- package/src/three/authoring/constraint-inspector-section.tsx +189 -0
- package/src/three/authoring/design-time-renderer.ts +189 -0
- package/src/three/authoring/model-asset-inspector-section.css +41 -0
- package/src/three/authoring/model-asset-inspector-section.tsx +869 -0
- package/src/three/authoring/oid-source-persistence.ts +557 -0
- package/src/three/authoring/r3f-design-session.ts +1174 -0
- package/src/three/authoring/r3f-source-authoring-adapter.ts +5949 -0
- package/src/three/authoring/reflection-probe-inspector-section.tsx +76 -0
- package/src/three/authoring/spatial-audio-handles.ts +215 -0
- package/src/three/authoring/spatial-collider-handles.ts +229 -0
- package/src/three/authoring/spatial-joint-handles.ts +114 -0
- package/src/three/authoring/spatial-light-handles.ts +178 -0
- package/src/three/authoring/spatial-lod-handles.ts +93 -0
- package/src/three/authoring/spatial-particle-handles.ts +368 -0
- package/src/three/authoring/three-authoring-adapter.ts +1953 -0
- package/src/three/authoring/three-scene-identity.ts +19 -0
- package/src/three/authoring/three-spatial-handles.ts +161 -0
- package/src/three/authoring/typed-three-inspector.ts +528 -0
- package/src/three/component-verbs/extract-menu.ts +71 -0
- package/src/three/component-verbs/fork-menu.ts +78 -0
- package/src/three/component-verbs/internals-menu.ts +91 -0
- package/src/three/story-documents/three-story-documents.tsx +607 -0
- package/src/three/three-board/ThreeBoardDocument.tsx +888 -0
- package/src/three/three-board/board-framing.ts +412 -0
- package/src/three/three-board/board-layout.ts +401 -0
- package/src/three/three-board/board-scene.ts +901 -0
- package/src/three/three-board/three-component-board.ts +62 -0
- package/src/xstate/XStateBehaviorSection.tsx +130 -0
- package/src/xstate/XStateMachineInspector.tsx +566 -0
- package/src/xstate/character-animation-machine.fixture.ts +74 -0
- package/src/xstate/live-behaviors.ts +106 -0
- package/src/xstate/use-live-actor-state.ts +44 -0
- package/src/xstate/xstate-graph.ts +235 -0
- package/src/xstate/xstate-layout.ts +76 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { type ProfilerViewId, profilerView } from '../host/components/utility-view-state';
|
|
2
|
+
import { EditorTab, EditorTabList } from '@volter/editor-sdk/widgets';
|
|
3
|
+
import { useSyncExternalStore } from 'react';
|
|
4
|
+
import { FrameDebuggerPanel } from './FrameDebuggerPanel';
|
|
5
|
+
import { PerformancePanel } from './PerformancePanel';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The engine's instrument bench: frame cost over time (Profiler) and the
|
|
9
|
+
* single-frame draw-call capture (Frame). Both read the host's own render
|
|
10
|
+
* path, which is why they share one utility — the game's debug plane
|
|
11
|
+
* (providers/commands) is a different audience and lives in its own.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const TABS: ReadonlyArray<{ id: ProfilerViewId; label: string }> = [
|
|
15
|
+
{ id: 'profiler', label: 'Profiler' },
|
|
16
|
+
{ id: 'frame', label: 'Frame' },
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
export function ProfilerPanel() {
|
|
20
|
+
useSyncExternalStore(profilerView.subscribe, profilerView.version, profilerView.version);
|
|
21
|
+
const active = profilerView.active();
|
|
22
|
+
return (
|
|
23
|
+
<div
|
|
24
|
+
data-testid="profiler-panel"
|
|
25
|
+
style={{
|
|
26
|
+
// §2.31: no root fill — the dock surface shows through.
|
|
27
|
+
flex: 1,
|
|
28
|
+
minHeight: 0,
|
|
29
|
+
display: 'flex',
|
|
30
|
+
flexDirection: 'column',
|
|
31
|
+
}}
|
|
32
|
+
>
|
|
33
|
+
<EditorTabList aria-label="Profiler views">
|
|
34
|
+
{TABS.map((tab) => (
|
|
35
|
+
<EditorTab
|
|
36
|
+
key={tab.id}
|
|
37
|
+
selected={active === tab.id}
|
|
38
|
+
data-testid={`profiler-tab-${tab.id}`}
|
|
39
|
+
onClick={() => profilerView.select(tab.id)}
|
|
40
|
+
>
|
|
41
|
+
{tab.label}
|
|
42
|
+
</EditorTab>
|
|
43
|
+
))}
|
|
44
|
+
</EditorTabList>
|
|
45
|
+
<div role="tabpanel" style={{ flex: 1, minHeight: 0, display: 'flex' }}>
|
|
46
|
+
{active === 'profiler' && <PerformancePanel />}
|
|
47
|
+
{/* W4b: the Frame debugger (single-frame draw-call capture over the
|
|
48
|
+
RenderDebugAdapter) renders its own honest no-adapter notice. */}
|
|
49
|
+
{active === 'frame' && <FrameDebuggerPanel />}
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared capture ring for the Frame debugger (W4b, F11). A single module store
|
|
3
|
+
* (the `utility-view-state.ts` idiom) so the TWO entry points that trigger a
|
|
4
|
+
* capture — the Game-tab `game-capture-frame` button and the Frame debugger
|
|
5
|
+
* panel's own `frame-capture-button` — append to and read from ONE bounded
|
|
6
|
+
* history. Without this, a capture fired from the Game tab would be lost to the
|
|
7
|
+
* panel it opens.
|
|
8
|
+
*
|
|
9
|
+
* HONESTY: `captureFrame` awaits the adapter's real capture, which REJECTS (it
|
|
10
|
+
* never fabricates) when no frame renders within the bounded timeout — a
|
|
11
|
+
* paused/stopped world. That rejection surfaces as `lastCaptureError`, shown
|
|
12
|
+
* verbatim in the panel. The ring is reset on adapter identity change (a new
|
|
13
|
+
* game mounted / play stopped) — captures from a prior session are stale.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { CaptureHistoryModel } from '../host/components/frame-debugger-model';
|
|
17
|
+
import type { RenderDebugAdapter } from '@volter/editor-project/adapter';
|
|
18
|
+
|
|
19
|
+
let history = new CaptureHistoryModel();
|
|
20
|
+
let boundAdapter: RenderDebugAdapter | null = null;
|
|
21
|
+
let capturing = false;
|
|
22
|
+
let lastError: string | null = null;
|
|
23
|
+
let version = 0;
|
|
24
|
+
const listeners = new Set<() => void>();
|
|
25
|
+
|
|
26
|
+
function notify(): void {
|
|
27
|
+
version++;
|
|
28
|
+
for (const listener of listeners) listener();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function subscribeFrameDebugger(listener: () => void): () => void {
|
|
32
|
+
listeners.add(listener);
|
|
33
|
+
return () => listeners.delete(listener);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Monotonic version — the `getSnapshot` for `useSyncExternalStore`. */
|
|
37
|
+
export function frameDebuggerVersion(): number {
|
|
38
|
+
return version;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Reset the shared ring when the bound adapter identity changes (new mount /
|
|
42
|
+
* play stopped). Idempotent for the same identity, so it is safe to call on
|
|
43
|
+
* every render/tick. */
|
|
44
|
+
export function syncFrameDebuggerAdapter(adapter: RenderDebugAdapter | null): void {
|
|
45
|
+
if (adapter === boundAdapter) return;
|
|
46
|
+
boundAdapter = adapter;
|
|
47
|
+
history = new CaptureHistoryModel();
|
|
48
|
+
lastError = null;
|
|
49
|
+
notify();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function getCaptureHistory(): CaptureHistoryModel {
|
|
53
|
+
return history;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function isCapturing(): boolean {
|
|
57
|
+
return capturing;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function lastCaptureError(): string | null {
|
|
61
|
+
return lastError;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function selectCapture(id: number): void {
|
|
65
|
+
history.select(id);
|
|
66
|
+
notify();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function clearCaptures(): void {
|
|
70
|
+
history.clear();
|
|
71
|
+
lastError = null;
|
|
72
|
+
notify();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Capture one frame through the adapter and append it to the shared ring.
|
|
77
|
+
* Binds the adapter (resetting a stale ring) first. A rejection — the honest
|
|
78
|
+
* "no frame rendered" of a paused/stopped world — is recorded as
|
|
79
|
+
* `lastCaptureError`; nothing is fabricated. No-op while a capture is already
|
|
80
|
+
* in flight or when no adapter is present.
|
|
81
|
+
*/
|
|
82
|
+
export async function captureFrame(adapter: RenderDebugAdapter | null): Promise<void> {
|
|
83
|
+
if (!adapter || capturing) return;
|
|
84
|
+
syncFrameDebuggerAdapter(adapter);
|
|
85
|
+
capturing = true;
|
|
86
|
+
lastError = null;
|
|
87
|
+
notify();
|
|
88
|
+
try {
|
|
89
|
+
const capture = await adapter.captureFrame();
|
|
90
|
+
history.add(capture);
|
|
91
|
+
} catch (error) {
|
|
92
|
+
lastError = error instanceof Error ? error.message : String(error);
|
|
93
|
+
} finally {
|
|
94
|
+
capturing = false;
|
|
95
|
+
notify();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main-thread busy sampling for the Profiler's frame-budget accounting.
|
|
3
|
+
*
|
|
4
|
+
* The per-source profiler measures only its own loop's rAF callback. The
|
|
5
|
+
* frame budget also needs "how long did OTHER main-thread tasks run" — React
|
|
6
|
+
* commits, timers, observers — which the browser only attributes above the
|
|
7
|
+
* 50ms long-animation-frame threshold. Below it, the honest measurable is
|
|
8
|
+
* event-loop responsiveness: a self-rescheduling `setTimeout(0)` records the
|
|
9
|
+
* gap between wakeups. Chrome clamps nested zero timeouts to ~4ms, so the
|
|
10
|
+
* SMALLEST gap in a window is the scheduler's baseline and anything above it
|
|
11
|
+
* is time the thread was busy with someone else's work (including the
|
|
12
|
+
* measured loop itself — callers subtract the loop's own measured CPU).
|
|
13
|
+
*
|
|
14
|
+
* Honesty properties:
|
|
15
|
+
* - The baseline is measured per window, never assumed, so throttling changes
|
|
16
|
+
* (background tab, battery saver) recalibrate instead of fabricating load.
|
|
17
|
+
* - Sustained saturation inflates every gap INCLUDING the minimum, so this
|
|
18
|
+
* under-reports rather than over-reports busy time. It is a floor.
|
|
19
|
+
* - Cost: ~250 timer wakeups/s of a few µs each — an opt-in instrument that
|
|
20
|
+
* runs only while the Profiler surface has it started, covered by the
|
|
21
|
+
* panel's existing "profiling adds measurement overhead" warning.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
interface GapSample {
|
|
25
|
+
/** performance.now() at the wakeup that OBSERVED the gap (its end). */
|
|
26
|
+
readonly t: number;
|
|
27
|
+
readonly gapMs: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Pure aggregation (unit-tested): total blocked time across `gaps`, using the
|
|
32
|
+
* window's smallest gap as the scheduler baseline. Fewer than 4 samples is
|
|
33
|
+
* too little signal to call anything busy — returns 0, never a guess.
|
|
34
|
+
*/
|
|
35
|
+
export function busyMsFromGaps(gaps: readonly number[]): number {
|
|
36
|
+
if (gaps.length < 4) return 0;
|
|
37
|
+
let baseline = Number.POSITIVE_INFINITY;
|
|
38
|
+
for (const gap of gaps) baseline = Math.min(baseline, gap);
|
|
39
|
+
let busy = 0;
|
|
40
|
+
for (const gap of gaps) busy += Math.max(0, gap - baseline);
|
|
41
|
+
return busy;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const MAX_SAMPLES = 1200;
|
|
45
|
+
|
|
46
|
+
export interface MainThreadBusySampler {
|
|
47
|
+
start(): void;
|
|
48
|
+
stop(): void;
|
|
49
|
+
/**
|
|
50
|
+
* Busy ms observed in [sinceMs, now], with the sample count so callers can
|
|
51
|
+
* report honest absence (`null` budget row) instead of a zero when the
|
|
52
|
+
* sampler has not covered the window.
|
|
53
|
+
*/
|
|
54
|
+
busySince(sinceMs: number): { busyMs: number; sampleCount: number };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function createMainThreadBusySampler(): MainThreadBusySampler {
|
|
58
|
+
const samples: GapSample[] = [];
|
|
59
|
+
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
60
|
+
let last = 0;
|
|
61
|
+
|
|
62
|
+
const tick = (): void => {
|
|
63
|
+
const now = performance.now();
|
|
64
|
+
samples.push({ t: now, gapMs: now - last });
|
|
65
|
+
if (samples.length > MAX_SAMPLES) samples.splice(0, samples.length - MAX_SAMPLES);
|
|
66
|
+
last = now;
|
|
67
|
+
timer = setTimeout(tick, 0);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
start() {
|
|
72
|
+
if (timer !== null) return;
|
|
73
|
+
samples.length = 0;
|
|
74
|
+
last = performance.now();
|
|
75
|
+
timer = setTimeout(tick, 0);
|
|
76
|
+
},
|
|
77
|
+
stop() {
|
|
78
|
+
if (timer === null) return;
|
|
79
|
+
clearTimeout(timer);
|
|
80
|
+
timer = null;
|
|
81
|
+
},
|
|
82
|
+
busySince(sinceMs: number) {
|
|
83
|
+
const gaps: number[] = [];
|
|
84
|
+
for (const sample of samples) {
|
|
85
|
+
if (sample.t >= sinceMs) gaps.push(sample.gapMs);
|
|
86
|
+
}
|
|
87
|
+
return { busyMs: busyMsFromGaps(gaps), sampleCount: gaps.length };
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|