@dailies-studio/shot-editor 0.1.0 → 0.2.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/dist/.vite/manifest.json +382 -0
- package/dist/assets/AgentPanel-CQejLbiX.js +2 -0
- package/dist/assets/AgentPanel-DJEphah0.css +1 -0
- package/dist/assets/AgentQuestionDialog-BqkD0kXF.js +1 -0
- package/dist/assets/CodeStudio-Dv4ihHpz.js +23 -0
- package/dist/assets/CommentsPanel-B4yhhT1T.js +1 -0
- package/dist/assets/ComponentLibraryRail-Ch_dsAru.js +1 -0
- package/dist/assets/ComponentStage-BRhRotyH.js +1 -0
- package/dist/assets/CurvePane-BjFVabKX.css +1 -0
- package/dist/assets/CurvePane-R22WzllJ.js +1 -0
- package/dist/assets/ExportDialog-CWLQWTmG.js +2 -0
- package/dist/assets/FontPicker-DWKaYL7F.js +1 -0
- package/dist/assets/FontPicker-v0IOIArM.css +1 -0
- package/dist/assets/GenerateDrawer-Bh3SqDKt.js +2 -0
- package/dist/assets/InsertDrawer-Ce1fod1L.js +1 -0
- package/dist/assets/LibraryBrowser-D9sEGz5w.js +2 -0
- package/dist/assets/MakeItYoursSection-CMO4319_.js +2 -0
- package/dist/assets/MaterialGraphCanvas-BFzl4wWR.js +23 -0
- package/dist/assets/MaterialGraphCanvas-BIUD59ik.css +1 -0
- package/dist/assets/MaterialWorkspace-CqTWqaRA.js +14 -0
- package/dist/assets/PreferencesDialog-CBf9_OoD.js +1 -0
- package/dist/assets/PresetsSection-CUEaoCVU.js +1 -0
- package/dist/assets/SoundHistory-BeS8EW1h.js +1 -0
- package/dist/assets/SoundMode-BmcZru3i.js +3 -0
- package/dist/assets/StockSounds-DdAUGRYK.js +1 -0
- package/dist/assets/TemplateFieldSection-CaeXpLFo.js +1 -0
- package/dist/assets/VersionHistoryPanel-CxG6XhNP.js +1 -0
- package/dist/assets/board-B88EPuLO.css +1 -0
- package/dist/assets/board-CL-he7SL.js +3 -0
- package/dist/assets/components-0oOSPN0l.js +1 -0
- package/dist/assets/googleFontRanges-gp32wJMo.js +1 -0
- package/dist/assets/googleFonts-BNon6M7X.js +1 -0
- package/dist/assets/iconsAndAliases-DL7gXP5j.js +1863 -0
- package/dist/assets/index-4F5NQmI8.css +1 -0
- package/dist/assets/index-BGHQGV5z.js +117 -0
- package/dist/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- package/dist/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- package/dist/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- package/dist/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- package/dist/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- package/dist/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- package/dist/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- package/dist/assets/libraryModel-CpIwfFha.js +1 -0
- package/dist/assets/lucide-6iK64tTB.js +9319 -0
- package/dist/assets/motionStamp-CH-Hew4E.js +1 -0
- package/dist/assets/takeMeasure-Tdg0TxEi.js +1 -0
- package/dist/assets/webmcp-BsB9gxWC.js +2 -0
- package/dist/index.html +34 -3
- package/package.json +12 -9
- package/src/addFamilies.ts +66 -0
- package/src/api.ts +1631 -55
- package/src/app/App.tsx +977 -198
- package/src/app/lazyPanel.tsx +137 -0
- package/src/app/lazySurfaces.tsx +203 -0
- package/src/audio/index.tsx +41 -10
- package/src/audio/meter.ts +485 -0
- package/src/audio/meterMath.ts +273 -0
- package/src/audio/meterStore.ts +155 -0
- package/src/audio/offlineMix.ts +289 -0
- package/src/audio/preview.ts +928 -118
- package/src/bridge.ts +15 -5
- package/src/bridgeTypes.ts +1611 -36
- package/src/channels.ts +17 -32
- package/src/fonts/googleFonts.ts +4 -0
- package/src/hostApi.ts +174 -0
- package/src/icons.tsx +770 -0
- package/src/layerWords.ts +61 -0
- package/src/main.tsx +56 -4
- package/src/panels/ResizeHandle.tsx +33 -43
- package/src/panels/agent/AgentDots.tsx +36 -0
- package/src/panels/agent/AgentPanel.tsx +119 -43
- package/src/panels/agent/AgentQuestionDialog.tsx +202 -0
- package/src/panels/agent/agent.css +113 -97
- package/src/panels/agent/agentGate.ts +35 -0
- package/src/panels/code/CodeDefaultsPanel.tsx +345 -0
- package/src/panels/code/CodeStudio.tsx +373 -0
- package/src/panels/code/FileTree.tsx +265 -0
- package/src/panels/code/codeBufferGuard.ts +52 -0
- package/src/panels/code/codeParts.tsx +321 -0
- package/src/panels/comments/CommentComposer.tsx +239 -0
- package/src/panels/comments/CommentThread.tsx +346 -0
- package/src/panels/comments/CommentsPanel.tsx +336 -0
- package/src/panels/comments/InboxButton.tsx +131 -0
- package/src/panels/comments/MentionMenu.tsx +98 -0
- package/src/panels/comments/parts.tsx +171 -0
- package/src/panels/export/ExportDialog.tsx +919 -96
- package/src/panels/export/ExportScrim.tsx +57 -0
- package/src/panels/files/AssetsTab.tsx +656 -0
- package/src/panels/files/FilePanel.tsx +1466 -216
- package/src/panels/files/LayerMarks.tsx +40 -0
- package/src/panels/files/LeftColumn.tsx +37 -0
- package/src/panels/files/McpSection.tsx +690 -47
- package/src/panels/files/MissingAssetsNotice.tsx +60 -0
- package/src/panels/files/assetDrag.ts +79 -0
- package/src/panels/files/resourceParts.tsx +255 -0
- package/src/panels/fonts/FontPicker.tsx +762 -0
- package/src/panels/fonts/faceCache.ts +207 -0
- package/src/panels/fonts/fontPicker.css +271 -0
- package/src/panels/fonts/pickerModel.ts +265 -0
- package/src/panels/fonts/pickerRequest.ts +46 -0
- package/src/panels/header/AccountAvatar.tsx +76 -0
- package/src/panels/header/AgentChip.tsx +77 -0
- package/src/panels/header/AppMenu.tsx +213 -0
- package/src/panels/header/Header.tsx +564 -84
- package/src/panels/header/HistoryControls.tsx +31 -5
- package/src/panels/header/PresenceStack.tsx +329 -0
- package/src/panels/header/ProjectTitle.tsx +232 -0
- package/src/panels/header/ShareButton.tsx +59 -0
- package/src/panels/header/StatusBadges.tsx +358 -27
- package/src/panels/header/SyncDeletedDialog.tsx +88 -0
- package/src/panels/header/ZoomControls.tsx +46 -15
- package/src/panels/header/agentHandoff.ts +52 -0
- package/src/panels/header/leave.ts +26 -0
- package/src/panels/help/HelpSection.tsx +189 -163
- package/src/panels/insert/InsertDrawer.tsx +723 -0
- package/src/panels/insert/iconSet.ts +109 -0
- package/src/panels/inspector/ExposeDot.tsx +117 -0
- package/src/panels/inspector/FramingRows.tsx +314 -44
- package/src/panels/inspector/InspectorFooter.tsx +91 -0
- package/src/panels/inspector/InspectorHead.tsx +425 -0
- package/src/panels/inspector/InspectorPanel.tsx +408 -124
- package/src/panels/inspector/KeyframeDot.tsx +373 -0
- package/src/panels/inspector/KeyframeSection.tsx +257 -0
- package/src/panels/inspector/MarkerSection.tsx +55 -11
- package/src/panels/inspector/ParamRow.tsx +84 -53
- package/src/panels/inspector/RightColumn.tsx +152 -0
- package/src/panels/inspector/TimeSection.tsx +866 -0
- package/src/panels/inspector/animate/EaseControl.tsx +244 -0
- package/src/panels/inspector/animate/PresetsSection.tsx +135 -0
- package/src/panels/inspector/animate/ShotSection.tsx +562 -48
- package/src/panels/inspector/design/AlignRow.tsx +95 -31
- package/src/panels/inspector/design/AppearanceSection.tsx +118 -9
- package/src/panels/inspector/design/ClonerSection.tsx +323 -62
- package/src/panels/inspector/design/ComponentSection.tsx +50 -43
- package/src/panels/inspector/design/CornerSection.tsx +45 -14
- package/src/panels/inspector/design/DefinitionProps.tsx +315 -0
- package/src/panels/inspector/design/DefinitionSection.tsx +136 -0
- package/src/panels/inspector/design/DeviceSection.tsx +210 -0
- package/src/panels/inspector/design/FillSection.tsx +83 -51
- package/src/panels/inspector/design/FontField.tsx +196 -0
- package/src/panels/inspector/design/HtmlSection.tsx +23 -12
- package/src/panels/inspector/design/InstanceSection.tsx +373 -154
- package/src/panels/inspector/design/MaskSection.tsx +84 -0
- package/src/panels/inspector/design/MaterialSection.tsx +177 -0
- package/src/panels/inspector/design/MediaSection.tsx +273 -120
- package/src/panels/inspector/design/MeshSection.tsx +910 -82
- package/src/panels/inspector/design/PaintRows.tsx +550 -0
- package/src/panels/inspector/design/PlateStyleSection.tsx +177 -0
- package/src/panels/inspector/design/ShadowSection.tsx +221 -48
- package/src/panels/inspector/design/SplineFollowSection.tsx +235 -0
- package/src/panels/inspector/design/StrokeSection.tsx +228 -43
- package/src/panels/inspector/design/TemplateFieldSection.tsx +57 -0
- package/src/panels/inspector/design/TransformSection.tsx +244 -50
- package/src/panels/inspector/design/TypographySection.tsx +679 -110
- package/src/panels/inspector/layout.ts +295 -0
- package/src/panels/inspector/propValue.ts +199 -0
- package/src/panels/inspector/scene/CameraSection.tsx +232 -22
- package/src/panels/inspector/scene/EffectsSection.tsx +722 -71
- package/src/panels/inspector/scene/MakeItYoursSection.tsx +180 -0
- package/src/panels/inspector/scene/SceneDocSection.tsx +191 -0
- package/src/panels/inspector/scene/SceneSection.tsx +219 -44
- package/src/panels/inspector/scene/TransitionSection.tsx +523 -72
- package/src/panels/inspector/storyboard/StoryboardReferenceSection.tsx +593 -0
- package/src/panels/inspector/storyboard/VideoStill.tsx +120 -0
- package/src/panels/inspector/timeModel.ts +190 -0
- package/src/panels/inspector/useDocWrite.ts +438 -146
- package/src/panels/library/LibraryBrowser.tsx +435 -0
- package/src/panels/library/LibraryGrid.tsx +190 -0
- package/src/panels/library/LibraryPreviewPanel.tsx +349 -0
- package/src/panels/library/LibraryRail.tsx +84 -0
- package/src/panels/library/LibrarySoundPanel.tsx +293 -0
- package/src/panels/library/SoundHistory.tsx +245 -0
- package/src/panels/library/StockSounds.tsx +390 -0
- package/src/panels/library/libraryModel.ts +147 -0
- package/src/panels/library/stockSearch.ts +77 -0
- package/src/panels/maskMarks.tsx +117 -0
- package/src/panels/material/MaterialCommentNode.tsx +54 -0
- package/src/panels/material/MaterialGraphCanvas.tsx +861 -0
- package/src/panels/material/MaterialGraphMenu.tsx +45 -0
- package/src/panels/material/MaterialGroupNode.tsx +56 -0
- package/src/panels/material/MaterialLibrary.tsx +253 -0
- package/src/panels/material/MaterialNode.tsx +453 -0
- package/src/panels/material/MaterialNodePalette.tsx +349 -0
- package/src/panels/material/MaterialParameterControls.tsx +525 -0
- package/src/panels/material/MaterialRerouteNode.tsx +42 -0
- package/src/panels/material/MaterialWorkspace.tsx +501 -0
- package/src/panels/material/NodePreview.tsx +70 -0
- package/src/panels/material/flowModel.ts +441 -0
- package/src/panels/material/parameterValues.ts +231 -0
- package/src/panels/material/previewScheduler.ts +235 -0
- package/src/panels/material/reactFlow.css +6 -0
- package/src/panels/material/socketGlyph.ts +85 -0
- package/src/panels/material/viewportMemory.ts +58 -0
- package/src/panels/preferences/PreferencesDialog.tsx +215 -0
- package/src/panels/share/ShareComponentDialog.tsx +247 -0
- package/src/panels/stage/StageMenu.tsx +35 -5
- package/src/panels/stage/StagePanel.tsx +338 -23
- package/src/panels/stage/cellSlots.tsx +70 -0
- package/src/panels/storyboard/BoardCanvas.tsx +739 -0
- package/src/panels/storyboard/BoardCursors.tsx +158 -0
- package/src/panels/storyboard/BoardDropDialog.tsx +161 -0
- package/src/panels/storyboard/BoardPlan.tsx +42 -0
- package/src/panels/storyboard/ComponentSheet.tsx +187 -0
- package/src/panels/storyboard/ComponentStage.tsx +235 -0
- package/src/panels/storyboard/MiniSketch.tsx +173 -0
- package/src/panels/storyboard/PartPicker.tsx +64 -0
- package/src/panels/storyboard/PlanPanel.tsx +617 -0
- package/src/panels/storyboard/ReferencePreview.tsx +217 -0
- package/src/panels/storyboard/ShotBrief.tsx +360 -0
- package/src/panels/storyboard/ShotCard.tsx +251 -0
- package/src/panels/storyboard/StagePreview.tsx +348 -0
- package/src/panels/storyboard/StillThumbs.tsx +162 -0
- package/src/panels/storyboard/StoryboardView.tsx +79 -0
- package/src/panels/storyboard/board.css +1065 -0
- package/src/panels/storyboard/board.ts +12 -0
- package/src/panels/storyboard/boardClipboard.ts +111 -0
- package/src/panels/storyboard/boardOrder.ts +68 -0
- package/src/panels/storyboard/boardRows.tsx +425 -0
- package/src/panels/storyboard/liveCap.ts +90 -0
- package/src/panels/storyboard/plan.css +295 -0
- package/src/panels/storyboard/posterCache.ts +133 -0
- package/src/panels/storyboard/shotboard/ComponentPicker.tsx +373 -0
- package/src/panels/storyboard/shotboard/ImagePicker.tsx +145 -0
- package/src/panels/storyboard/shotboard/ShotBoard.tsx +292 -0
- package/src/panels/storyboard/shotboard/SketchCanvas.tsx +443 -0
- package/src/panels/storyboard/shotboard/SketchInspector.tsx +471 -0
- package/src/panels/storyboard/shotboard/SketchItem.tsx +240 -0
- package/src/panels/storyboard/shotboard/SketchLayers.tsx +130 -0
- package/src/panels/storyboard/shotboard/SketchPlay.tsx +108 -0
- package/src/panels/storyboard/shotboard/SketchStrip.tsx +178 -0
- package/src/panels/storyboard/shotboard/SketchToolFooter.tsx +99 -0
- package/src/panels/storyboard/shotboard/sketch.css +1392 -0
- package/src/panels/storyboard/shotboard/sketchModel.ts +219 -0
- package/src/panels/storyboard/storyboardDrop.ts +164 -0
- package/src/panels/studio/ComponentLibraryRail.tsx +346 -0
- package/src/panels/studio/ComponentSidePanel.tsx +355 -0
- package/src/panels/studio/ComponentStudio.tsx +243 -0
- package/src/panels/studio/ComponentsHome.tsx +318 -0
- package/src/panels/studio/ComponentsPreview.tsx +179 -0
- package/src/panels/studio/EveryUse.tsx +244 -0
- package/src/panels/studio/ThisUsePanel.tsx +679 -0
- package/src/panels/studio/componentParts.tsx +341 -0
- package/src/panels/studio/components.ts +6 -0
- package/src/panels/studio/newCodeComponent.ts +66 -0
- package/src/panels/timeline/AudioFxButton.tsx +290 -0
- package/src/panels/timeline/AudioLane.tsx +674 -85
- package/src/panels/timeline/BlockRow.tsx +154 -85
- package/src/panels/timeline/ChannelRow.tsx +378 -129
- package/src/panels/timeline/CurvePane.tsx +508 -0
- package/src/panels/timeline/Filmstrip.tsx +361 -0
- package/src/panels/timeline/GenerateDrawer.tsx +407 -0
- package/src/panels/timeline/KeyStretch.tsx +115 -0
- package/src/panels/timeline/LifespanRow.tsx +730 -47
- package/src/panels/timeline/MarkerLane.tsx +46 -8
- package/src/panels/timeline/PlayButton.tsx +91 -0
- package/src/panels/timeline/PropertyPicker.tsx +254 -0
- package/src/panels/timeline/ShotRow.tsx +400 -0
- package/src/panels/timeline/SoundMode.tsx +1406 -0
- package/src/panels/timeline/TimelineDock.tsx +2338 -272
- package/src/panels/timeline/TransportVolume.tsx +163 -0
- package/src/panels/timeline/beatRuler.ts +74 -0
- package/src/panels/timeline/curveModel.ts +500 -0
- package/src/panels/timeline/duckUnderVoice.ts +68 -0
- package/src/panels/timeline/keyframeMenu.ts +138 -0
- package/src/panels/timeline/labelWidth.ts +49 -0
- package/src/panels/timeline/marquee.ts +155 -0
- package/src/panels/timeline/model.ts +1860 -199
- package/src/panels/timeline/snap.ts +125 -43
- package/src/panels/timeline/staggerPreview.tsx +92 -0
- package/src/panels/timeline/takeMeasure.ts +72 -0
- package/src/panels/timeline/useLive.ts +48 -0
- package/src/panels/timeline/viewport.ts +234 -0
- package/src/panels/toolbar/BackChip.tsx +116 -0
- package/src/panels/toolbar/CameraViewBar.tsx +118 -49
- package/src/panels/toolbar/CanvasBar.tsx +117 -0
- package/src/panels/toolbar/InsertButton.tsx +38 -0
- package/src/panels/toolbar/IsolationTrail.tsx +156 -0
- package/src/panels/toolbar/PanelToggles.tsx +86 -0
- package/src/panels/toolbar/ShadingBar.tsx +119 -26
- package/src/panels/toolbar/ToolDockBar.tsx +329 -80
- package/src/panels/toolbar/ToolFooter.tsx +102 -0
- package/src/panels/toolbar/ViewModeBar.tsx +247 -38
- package/src/panels/toolbar/WorkPlaneBar.tsx +94 -0
- package/src/panels/toolbar/useAddRows.tsx +326 -0
- package/src/panels/toolbar/useDismiss.ts +122 -0
- package/src/panels/versions/VersionHistoryPanel.tsx +274 -0
- package/src/panels/versions/versionModel.ts +103 -0
- package/src/panels/virtualWindow.ts +271 -0
- package/src/shotTokens.css +151 -60
- package/src/stage/FrustumGizmo.tsx +504 -84
- package/src/stage/GridPlane.tsx +225 -16
- package/src/stage/NodePreviews.tsx +628 -0
- package/src/stage/PerfHud.tsx +437 -47
- package/src/stage/SplitRenderer.tsx +816 -169
- package/src/stage/StageApp.tsx +4274 -486
- package/src/stage/StageRootBoundary.tsx +114 -0
- package/src/stage/StageTools.tsx +651 -151
- package/src/stage/TransformGizmo.tsx +696 -0
- package/src/stage/adaptivePresentation.ts +145 -0
- package/src/stage/bridgeClient.ts +2522 -41
- package/src/stage/exportWorker.ts +9 -0
- package/src/stage/feed.ts +305 -14
- package/src/stage/fontPreview.ts +176 -0
- package/src/stage/frustumGeometry.ts +319 -1
- package/src/stage/furnitureTokens.ts +33 -0
- package/src/stage/gizmoMath.ts +508 -16
- package/src/stage/gpuTimer.ts +276 -0
- package/src/stage/instanceModules.ts +194 -33
- package/src/stage/interactions/move.ts +24 -5
- package/src/stage/isolation.ts +291 -33
- package/src/stage/jsSelfProfiler.ts +325 -0
- package/src/stage/main.tsx +116 -16
- package/src/stage/modalState.ts +222 -23
- package/src/stage/moduleEpoch.ts +60 -0
- package/src/stage/motionPathStage.ts +279 -0
- package/src/stage/motionPreview.ts +74 -0
- package/src/stage/overlays/Overlays.tsx +1112 -159
- package/src/stage/overlays/Rulers.tsx +495 -0
- package/src/stage/panes.ts +159 -10
- package/src/stage/project.ts +68 -3
- package/src/stage/renderScaleController.ts +730 -0
- package/src/stage/rulers.ts +129 -0
- package/src/stage/sceneExtent.ts +75 -0
- package/src/stage/shading.ts +20 -0
- package/src/stage/splineStage.ts +632 -0
- package/src/stage/stagePerformanceTelemetry.ts +717 -0
- package/src/stage/stageTheme.ts +36 -0
- package/src/stage/stageWarmup.ts +11 -0
- package/src/stage/textAutoSize.ts +124 -0
- package/src/stage/tier1Export.ts +641 -0
- package/src/stage/tokenColor.ts +14 -9
- package/src/stage/view.ts +56 -9
- package/src/stage/workingView.ts +277 -33
- package/src/state/accountPrefs.ts +261 -0
- package/src/state/agentActions.ts +173 -15
- package/src/state/agentActivity.ts +198 -0
- package/src/state/agentQuestion.ts +239 -0
- package/src/state/bootOpen.ts +42 -0
- package/src/state/cameraVerbs.ts +139 -0
- package/src/state/clientId.ts +6 -1
- package/src/state/comments.ts +1099 -0
- package/src/state/context.tsx +19 -1
- package/src/state/editorPrefs.ts +220 -0
- package/src/state/errorReports.ts +121 -0
- package/src/state/fieldWrite.ts +161 -0
- package/src/state/fontRecents.ts +70 -0
- package/src/state/fonts.ts +656 -0
- package/src/state/graphClipboard.ts +414 -0
- package/src/state/imageThumbs.ts +216 -0
- package/src/state/insert.ts +444 -67
- package/src/state/isolationTrail.ts +92 -0
- package/src/state/keyMoves.ts +134 -0
- package/src/state/keyframeClipboard.ts +177 -0
- package/src/state/keymap.ts +1153 -117
- package/src/state/layoutPrefs.ts +272 -0
- package/src/state/layouts.ts +205 -0
- package/src/state/measureMedia.ts +167 -0
- package/src/state/menu.ts +2341 -28
- package/src/state/motionStamp.ts +7 -0
- package/src/state/panelSize.ts +375 -5
- package/src/state/pendingChanges.ts +119 -0
- package/src/state/playHold.ts +78 -0
- package/src/state/playbackPreparation.ts +562 -0
- package/src/state/posterCapture.ts +242 -0
- package/src/state/presence.ts +254 -0
- package/src/state/projectComponents.ts +160 -0
- package/src/state/renderEvents.ts +298 -18
- package/src/state/runtimeErrors.ts +46 -0
- package/src/state/selection.ts +12 -0
- package/src/state/shortcuts.ts +1606 -0
- package/src/state/sketchGuide.ts +51 -0
- package/src/state/splineRefs.ts +87 -0
- package/src/state/stageHealth.ts +259 -0
- package/src/state/stageWatchdog.ts +47 -0
- package/src/state/stillStage.ts +330 -0
- package/src/state/stillThumbs.ts +474 -0
- package/src/state/store.ts +23591 -2982
- package/src/state/storyboardModel.ts +528 -0
- package/src/state/sync.ts +1257 -0
- package/src/state/syncCache.ts +159 -0
- package/src/state/triangles.ts +13 -0
- package/src/state/types.ts +1698 -55
- package/src/state/unsavedGuard.ts +49 -0
- package/src/state/viewMode.ts +200 -0
- package/src/state/webmcp.ts +1128 -0
- package/src/state/workspace.ts +1147 -0
- package/src/styles/assets-tab.css +304 -0
- package/src/styles/canvas.css +545 -0
- package/src/styles/comments.css +1435 -0
- package/src/styles/controls.css +162 -0
- package/src/styles/dialogs.css +390 -0
- package/src/styles/insert-drawer.css +586 -0
- package/src/styles/inspector-board.css +207 -0
- package/src/styles/inspector-object.css +486 -0
- package/src/styles/inspector-shot.css +252 -0
- package/src/styles/inspector.css +360 -0
- package/src/styles/left-column.css +1628 -0
- package/src/styles/library-preview.css +575 -0
- package/src/styles/library.css +512 -0
- package/src/styles/modes/code.css +569 -0
- package/src/styles/modes/component.css +1561 -0
- package/src/styles/modes/material.css +1201 -0
- package/src/styles/modes/storyboard.css +518 -0
- package/src/styles/share-component.css +49 -0
- package/src/styles/shell.css +653 -0
- package/src/styles/shot-brief.css +360 -0
- package/src/styles/states.css +76 -0
- package/src/styles/sync.css +380 -0
- package/src/styles/timeline-graph.css +384 -0
- package/src/styles/timeline-meters.css +89 -0
- package/src/styles/timeline-rows.css +1959 -0
- package/src/styles/timeline-sound.css +1003 -0
- package/src/styles/timeline.css +754 -0
- package/src/styles/tool-footer.css +525 -0
- package/src/styles/topbar.css +886 -0
- package/src/styles/versions.css +262 -0
- package/src/styles.css +72 -2921
- package/src/tools.ts +108 -23
- package/dist/assets/index-B7notge7.css +0 -1
- package/dist/assets/index-BBE6Ta9L.js +0 -52
- package/src/panels/files/icons.tsx +0 -118
- package/src/panels/header/DocSwitcher.tsx +0 -85
- package/src/panels/header/TransportBar.tsx +0 -86
- package/src/panels/inspector/animate/BlockTiming.tsx +0 -341
- package/src/panels/inspector/animate/DuringBlocks.tsx +0 -85
- package/src/panels/inspector/animate/InOutPresets.tsx +0 -57
- package/src/panels/inspector/animate/ResolvedChannels.tsx +0 -150
- package/src/panels/inspector/animate/useBlocks.ts +0 -43
- package/src/panels/material/MaterialPicker.tsx +0 -172
- package/src/panels/timeline/SceneStrip.tsx +0 -145
- package/src/panels/toolbar/AddMenu.tsx +0 -230
- package/src/stage/MoveGizmo.tsx +0 -411
package/src/stage/PerfHud.tsx
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
//
|
|
3
3
|
// Exists because every performance judgement about this lane used to be made
|
|
4
4
|
// by reading code: there was no way to SEE where a frame went. The HUD prints
|
|
5
|
-
// the
|
|
5
|
+
// the six numbers that decide whether the stage is healthy, sampled on a
|
|
6
6
|
// half-second interval so the HUD itself costs nothing worth measuring:
|
|
7
7
|
//
|
|
8
8
|
// gl/s GL frames drawn per second. Under `frameloop="never"` a draw IS
|
|
@@ -11,26 +11,147 @@
|
|
|
11
11
|
// ui ms EMA of the gap between rAF ticks — main-thread health. 16.7 is
|
|
12
12
|
// a clean 60Hz; spikes mean something synchronous is eating the
|
|
13
13
|
// thread (a raster storm, a full-dock reconcile).
|
|
14
|
-
//
|
|
15
|
-
//
|
|
14
|
+
// gpu the GPU's own time per frame, p50/p95 over the frames resolved
|
|
15
|
+
// since the last sample, off three's timestamp queries
|
|
16
|
+
// (`GpuTimer`, gpuTimer.ts); `n/a` when the renderer has no
|
|
17
|
+
// timer, `-` when nothing drew.
|
|
18
|
+
// scale the playback render scale (renderScaleController.ts): the share
|
|
19
|
+
// of the display's density a played frame renders at, the mode,
|
|
20
|
+
// a step waiting for its cut, the last judged window's GPU p75
|
|
21
|
+
// against its frame period and its frames that came late, and
|
|
22
|
+
// why it last moved.
|
|
23
|
+
// draws draw calls in the last frame, every render of it summed, off
|
|
24
|
+
// `gl.info` (see PerfProbe for the reset and the
|
|
25
|
+
// drawCalls-vs-calls renderer-class split).
|
|
16
26
|
// raster the module LRU: live MB (refcounted entries included — the
|
|
17
27
|
// honest live set), entry count, and hits/misses over the last
|
|
18
28
|
// interval. Misses during steady playback are a regression; see
|
|
19
29
|
// rasterCache.test.ts.
|
|
20
30
|
// layers how many layers the open document's current scene holds.
|
|
21
31
|
//
|
|
22
|
-
// Two halves, same file: `PerfProbe` runs INSIDE the canvas (
|
|
23
|
-
// the presenters, so its info read sees the finished
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
32
|
+
// Two halves, same file: `PerfProbe` runs INSIDE the canvas (the scheduler's
|
|
33
|
+
// finish phase, after the presenters, so its info read sees the finished
|
|
34
|
+
// frame) and writes into a mutable box, which StageApp's `GpuTimer` feeds
|
|
35
|
+
// too; `PerfHud` is DOM, polling the box and `rasterCacheStats()` on an
|
|
36
|
+
// interval. The box is the same feed pattern the picking lane uses: the probe
|
|
37
|
+
// must never cause a render, or the HUD would bend the number it shows.
|
|
27
38
|
import { useEffect, useRef, useState } from "react";
|
|
28
39
|
import { useFrame, useThree } from "@react-three/fiber";
|
|
29
|
-
import {
|
|
40
|
+
import {
|
|
41
|
+
canvasUploadStats,
|
|
42
|
+
enableShotPerf,
|
|
43
|
+
layerMaterialPoolStats,
|
|
44
|
+
planeCacheStats,
|
|
45
|
+
rasterCacheStats,
|
|
46
|
+
rendererCompileBusy,
|
|
47
|
+
shotPerfStats,
|
|
48
|
+
} from "@dailies-studio/shot";
|
|
49
|
+
import type { PresentationQuality } from "./adaptivePresentation.js";
|
|
50
|
+
import {
|
|
51
|
+
createJsSelfProfilerController,
|
|
52
|
+
isLocalJsSelfProfileOptIn,
|
|
53
|
+
} from "./jsSelfProfiler.js";
|
|
54
|
+
import type { JsSelfProfilerController } from "./jsSelfProfiler.js";
|
|
55
|
+
import type { StageInset } from "../bridgeTypes.js";
|
|
56
|
+
import { GpuSamples, NO_GPU_STATS } from "./gpuTimer.js";
|
|
57
|
+
import type { GpuStats, GpuTimerKind } from "./gpuTimer.js";
|
|
58
|
+
import type { RenderScaleController, RenderScaleReading } from "./renderScaleController.js";
|
|
59
|
+
import { stagePerformanceInterval, stagePerformanceLine } from "./stagePerformanceTelemetry.js";
|
|
60
|
+
import type {
|
|
61
|
+
StagePerformanceDebugSnapshot,
|
|
62
|
+
StagePresentationSample,
|
|
63
|
+
StagePerformanceTelemetry,
|
|
64
|
+
} from "./stagePerformanceTelemetry.js";
|
|
30
65
|
|
|
31
66
|
/** Read once: the stage URL is set at iframe mount and never rewritten. */
|
|
32
67
|
export const PERF_HUD_ENABLED =
|
|
33
|
-
typeof window !== "undefined" && new URLSearchParams(window.location.search).get("perf")
|
|
68
|
+
typeof window !== "undefined" && new URLSearchParams(window.location.search).get("perf") !== null;
|
|
69
|
+
|
|
70
|
+
/** `?perf=log` as well as the HUD: echo every sample to the console.
|
|
71
|
+
*
|
|
72
|
+
* OPT-IN SEPARATELY, because it is two lines a second forever and the console
|
|
73
|
+
* is also where three's own deprecation warnings and any real error land. The
|
|
74
|
+
* overlay and `window.__perf` cover reading a number; this covers watching one
|
|
75
|
+
* move over time, which is the rarer want. */
|
|
76
|
+
export const PERF_LOG_ENABLED =
|
|
77
|
+
typeof window !== "undefined" && new URLSearchParams(window.location.search).get("perf") === "log";
|
|
78
|
+
|
|
79
|
+
/** The stack sampler is stricter than the counter HUD: exact query value and
|
|
80
|
+
* a loopback document. A hosted page carrying a copied query remains inert. */
|
|
81
|
+
export const PERF_SELF_PROFILE_ENABLED =
|
|
82
|
+
typeof window !== "undefined" && isLocalJsSelfProfileOptIn(window.location.href);
|
|
83
|
+
|
|
84
|
+
// The runtime's own counters cost a `performance.now()` per draw, so they stay
|
|
85
|
+
// off until something asks. This is the only caller, and asking once at module
|
|
86
|
+
// evaluation means the very first frames are measured too — a compile storm on
|
|
87
|
+
// load is exactly the kind of thing that would hide behind a later switch-on.
|
|
88
|
+
if (PERF_HUD_ENABLED && !PERF_SELF_PROFILE_ENABLED) enableShotPerf();
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* The counters themselves, not the line: `window.__perfProbe`.
|
|
92
|
+
*
|
|
93
|
+
* `window.__perf` is a STRING sampled on an interval, and a hidden tab clamps
|
|
94
|
+
* that interval to about a second — useless for asking what one frame cost.
|
|
95
|
+
* The obvious answer, importing `@dailies-studio/shot` from the console and
|
|
96
|
+
* calling `shotPerfStats()`, does not work: Vite hands an `?t=` -stamped URL
|
|
97
|
+
* to the module graph and a console import of the bare path gets a SECOND
|
|
98
|
+
* instance, whose counters are all zero while the real ones move. That failure
|
|
99
|
+
* is silent and reads exactly like "the runtime is not drawing".
|
|
100
|
+
*
|
|
101
|
+
* So the HUD, which is already inside the stage and already holds the live
|
|
102
|
+
* bindings, publishes them. Behind the same flag as everything else here, so a
|
|
103
|
+
* shipped stage has no such property.
|
|
104
|
+
*/
|
|
105
|
+
/** The live renderer, parked by `PerfProbe` on its first frame.
|
|
106
|
+
*
|
|
107
|
+
* Module scope rather than a field on the box: `PerfProbe` is the only thing
|
|
108
|
+
* in the stage that already holds `gl`, and every question worth asking of it
|
|
109
|
+
* is a one-off from a console. Publishing the OBJECT rather than a chosen
|
|
110
|
+
* statistic is deliberate — the last three diagnostics each needed a different
|
|
111
|
+
* number off it, and each one cost a source edit and a reload to get. */
|
|
112
|
+
let probeRenderer: unknown = null;
|
|
113
|
+
|
|
114
|
+
interface WindowPerfProbe {
|
|
115
|
+
shot: typeof shotPerfStats;
|
|
116
|
+
raster: typeof rasterCacheStats;
|
|
117
|
+
planes: typeof planeCacheStats;
|
|
118
|
+
mats: typeof layerMaterialPoolStats;
|
|
119
|
+
uploads: typeof canvasUploadStats;
|
|
120
|
+
renderer: () => unknown;
|
|
121
|
+
/** A compile is queued or running on the live renderer
|
|
122
|
+
* (`rendererCompileBusy`): what a proof that steps frames one at a time
|
|
123
|
+
* waits out, so a pipeline made in a draw means nothing warmed it, whatever
|
|
124
|
+
* the host's speed. */
|
|
125
|
+
compileBusy: () => boolean;
|
|
126
|
+
stage?: (afterSequence?: number) => StagePerformanceDebugSnapshot | null;
|
|
127
|
+
stageSequence?: () => number | null;
|
|
128
|
+
subscribeStage?: (
|
|
129
|
+
listener: (sample: StagePresentationSample) => void,
|
|
130
|
+
) => (() => void) | null;
|
|
131
|
+
/** GPU time per frame over the frames whose resolve landed in
|
|
132
|
+
* `[fromMs, toMs)` (this document's `performance.now()`, every frame kept
|
|
133
|
+
* when absent), and which timer measured it (`pending` before the renderer
|
|
134
|
+
* was asked, `n/a` without one). */
|
|
135
|
+
gpu?: (fromMs?: number, toMs?: number) => GpuStats & { timer: PerfBox["gpuTimer"] };
|
|
136
|
+
profile?: JsSelfProfilerController;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const windowPerfProbe: WindowPerfProbe | null = PERF_HUD_ENABLED
|
|
140
|
+
? {
|
|
141
|
+
shot: shotPerfStats,
|
|
142
|
+
raster: rasterCacheStats,
|
|
143
|
+
planes: planeCacheStats,
|
|
144
|
+
mats: layerMaterialPoolStats,
|
|
145
|
+
uploads: canvasUploadStats,
|
|
146
|
+
renderer: () => probeRenderer,
|
|
147
|
+
compileBusy: () => probeRenderer !== null && typeof probeRenderer === "object" && rendererCompileBusy(probeRenderer),
|
|
148
|
+
...(PERF_SELF_PROFILE_ENABLED ? { profile: createJsSelfProfilerController() } : {}),
|
|
149
|
+
}
|
|
150
|
+
: null;
|
|
151
|
+
|
|
152
|
+
if (windowPerfProbe !== null) {
|
|
153
|
+
(window as unknown as { __perfProbe?: WindowPerfProbe }).__perfProbe = windowPerfProbe;
|
|
154
|
+
}
|
|
34
155
|
|
|
35
156
|
export interface PerfBox {
|
|
36
157
|
glFrames: number;
|
|
@@ -42,21 +163,53 @@ export interface PerfBox {
|
|
|
42
163
|
/**
|
|
43
164
|
* Triangles the renderer submitted, off `info.render.triangles`.
|
|
44
165
|
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* per-layer badges are the number to trust for authoring decisions; this one
|
|
50
|
-
* is for watching a pass get heavier while you drag.
|
|
166
|
+
* EVERY RENDER OF THE LAST FRAME, like `drawCalls` beside it: each bin, each
|
|
167
|
+
* post pass and both split panes, summed (PerfProbe owns the reset). The
|
|
168
|
+
* outliner's per-layer badges are the number to trust for authoring
|
|
169
|
+
* decisions; this one is for watching a frame get heavier while you drag.
|
|
51
170
|
*/
|
|
52
171
|
triangles: number;
|
|
172
|
+
/**
|
|
173
|
+
* Textures and geometries the renderer is HOLDING, off `info.memory`.
|
|
174
|
+
*
|
|
175
|
+
* The counterpart to `programs`, and the number that finally made a
|
|
176
|
+
* progressive slowdown visible. Every cache this lane owns reports itself
|
|
177
|
+
* (raster MB/entries, planes, mats), so when a document got slower on each
|
|
178
|
+
* successive pass while all of them stayed flat, there was nothing left to
|
|
179
|
+
* look at. These two are what an INSTANCE component's own textures and
|
|
180
|
+
* geometries land in, and nothing else on this line can see those.
|
|
181
|
+
*/
|
|
182
|
+
textures: number;
|
|
183
|
+
geometries: number;
|
|
53
184
|
/** Wall-time spans from a `markPerf()` call to the NEXT presented frame,
|
|
54
185
|
* keyed by the mark's name. The probe settles them; the HUD prints them. */
|
|
55
186
|
spans: Record<string, number>;
|
|
187
|
+
/** What `GpuTimer` found: `pending` until it has asked the renderer, then
|
|
188
|
+
* the backend it times, or `n/a` when the renderer cannot time a pass. */
|
|
189
|
+
gpuTimer: GpuTimerKind | "pending" | "n/a";
|
|
190
|
+
/** The frames `GpuTimer` resolved (`perfGpuFrame`), made at the first, so
|
|
191
|
+
* a stage without the HUD never allocates it. */
|
|
192
|
+
gpu: GpuSamples | null;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** One frame `GpuTimer` resolved, into the box's ring: what the HUD's `gpu`
|
|
196
|
+
* reading and `__perfProbe.gpu()` read. Nothing without the HUD. */
|
|
197
|
+
export function perfGpuFrame(box: PerfBox, ms: number): void {
|
|
198
|
+
if (PERF_HUD_ENABLED) (box.gpu ??= new GpuSamples()).push(performance.now(), ms);
|
|
56
199
|
}
|
|
57
200
|
|
|
58
201
|
export function createPerfBox(): PerfBox {
|
|
59
|
-
return {
|
|
202
|
+
return {
|
|
203
|
+
glFrames: 0,
|
|
204
|
+
drawCalls: 0,
|
|
205
|
+
programs: 0,
|
|
206
|
+
triangles: 0,
|
|
207
|
+
textures: 0,
|
|
208
|
+
geometries: 0,
|
|
209
|
+
spans: {},
|
|
210
|
+
gpuTimer: "pending",
|
|
211
|
+
gpu: null,
|
|
212
|
+
};
|
|
60
213
|
}
|
|
61
214
|
|
|
62
215
|
/**
|
|
@@ -72,38 +225,57 @@ const pendingMarks = new Map<string, number>();
|
|
|
72
225
|
/** Record "X happened; measure to the next presented frame". No-op unless the
|
|
73
226
|
* HUD is on, so callers do not need their own gate. */
|
|
74
227
|
export function markPerf(name: string): void {
|
|
75
|
-
if (!PERF_HUD_ENABLED) return;
|
|
228
|
+
if (!PERF_HUD_ENABLED || PERF_SELF_PROFILE_ENABLED) return;
|
|
76
229
|
pendingMarks.set(name, performance.now());
|
|
77
230
|
}
|
|
78
231
|
|
|
79
232
|
/** In-canvas half: counts GL frames and copies the renderer's draw-call count.
|
|
80
233
|
*
|
|
81
|
-
* THE
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
* the
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
|
|
234
|
+
* THE PHASE IS LOAD-BEARING. The probe subscribes in the scheduler's
|
|
235
|
+
* `finish` phase, which runs after the `render` phase, so it reads the
|
|
236
|
+
* FINISHED frame in every layout. fiber only stands its own draw down for
|
|
237
|
+
* jobs in the `render` phase (`hasUserJobsInPhase`) or a legacy positive
|
|
238
|
+
* priority, so a finish-phase probe can never leave the camera-only layout
|
|
239
|
+
* blank the way a positive-priority one could in v9. This replaces the v9
|
|
240
|
+
* conditional (priority 2 with a presenter mounted, else a one-frame-stale
|
|
241
|
+
* priority 0): the v10 phase gives the fresh reading unconditionally.
|
|
242
|
+
*
|
|
243
|
+
* AND THE PROBE OWNS THE RESET. three's Info clears its per-frame counters on
|
|
244
|
+
* its own rAF tick (`autoReset`), which has nothing to do with when this
|
|
245
|
+
* stage draws: two draws inside one tick are read as one frame, and a
|
|
246
|
+
* hidden tab, which gets no ticks, never resets at all. So the probe turns
|
|
247
|
+
* that off and clears the counters at the START of every frame, and the
|
|
248
|
+
* finish phase reads every render the frame made. At the start rather than
|
|
249
|
+
* after the read, because a reader that runs after the frame (the
|
|
250
|
+
* benchmark's resource rows, from the presentation callback) must still
|
|
251
|
+
* find it. */
|
|
252
|
+
export function PerfProbe({ box }: { box: PerfBox }): null {
|
|
92
253
|
const gl = useThree((s) => s.gl);
|
|
254
|
+
useEffect(() => {
|
|
255
|
+
const info = infoOf(gl);
|
|
256
|
+
if (PERF_SELF_PROFILE_ENABLED || info === undefined) return;
|
|
257
|
+
const previous = info.autoReset;
|
|
258
|
+
info.autoReset = false;
|
|
259
|
+
return () => {
|
|
260
|
+
info.autoReset = previous ?? true;
|
|
261
|
+
};
|
|
262
|
+
}, [gl]);
|
|
93
263
|
useFrame(() => {
|
|
264
|
+
if (!PERF_SELF_PROFILE_ENABLED) infoOf(gl)?.reset?.();
|
|
265
|
+
}, { phase: "start" });
|
|
266
|
+
useFrame(() => {
|
|
267
|
+
if (PERF_HUD_ENABLED && probeRenderer === null) probeRenderer = gl;
|
|
268
|
+
if (PERF_SELF_PROFILE_ENABLED) return;
|
|
94
269
|
box.glFrames += 1;
|
|
95
|
-
const info = (gl
|
|
96
|
-
info?: {
|
|
97
|
-
render?: { calls?: number; drawCalls?: number; triangles?: number };
|
|
98
|
-
memory?: { programs?: number };
|
|
99
|
-
};
|
|
100
|
-
}).info;
|
|
270
|
+
const info = infoOf(gl);
|
|
101
271
|
// The common WebGPURenderer Info spells the per-frame count `drawCalls`
|
|
102
272
|
// (its `calls` is a LIFETIME total); a classic WebGLRenderer has no
|
|
103
273
|
// `drawCalls` and its `calls` is per-frame. Hence this fallback order —
|
|
104
274
|
// the split is renderer class, not backend.
|
|
105
275
|
box.drawCalls = info?.render?.drawCalls ?? info?.render?.calls ?? 0;
|
|
106
276
|
box.programs = info?.memory?.programs ?? 0;
|
|
277
|
+
box.textures = info?.memory?.textures ?? 0;
|
|
278
|
+
box.geometries = info?.memory?.geometries ?? 0;
|
|
107
279
|
// Spelled the same on both renderer classes, so no fallback is needed here.
|
|
108
280
|
box.triangles = info?.render?.triangles ?? 0;
|
|
109
281
|
// Settle pending marks: this frame is the first one PRESENTED after the
|
|
@@ -113,19 +285,97 @@ export function PerfProbe({ box, presenterMounted }: { box: PerfBox; presenterMo
|
|
|
113
285
|
for (const [name, at] of pendingMarks) box.spans[name] = now - at;
|
|
114
286
|
pendingMarks.clear();
|
|
115
287
|
}
|
|
116
|
-
},
|
|
288
|
+
}, { phase: "finish" });
|
|
117
289
|
return null;
|
|
118
290
|
}
|
|
119
291
|
|
|
292
|
+
/** The slice of three's renderer `Info` the probe reads and resets. */
|
|
293
|
+
interface RendererInfo {
|
|
294
|
+
autoReset?: boolean;
|
|
295
|
+
reset?: () => void;
|
|
296
|
+
render?: { calls?: number; drawCalls?: number; triangles?: number };
|
|
297
|
+
memory?: { programs?: number; textures?: number; geometries?: number };
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function infoOf(gl: unknown): RendererInfo | undefined {
|
|
301
|
+
return (gl as { info?: RendererInfo }).info;
|
|
302
|
+
}
|
|
303
|
+
|
|
120
304
|
const SAMPLE_MS = 500;
|
|
121
305
|
|
|
306
|
+
/** The HUD's `scale` segment. */
|
|
307
|
+
export function renderScaleText(reading: RenderScaleReading): string {
|
|
308
|
+
const next = reading.next === null ? "" : `→${reading.next.toFixed(2)}`;
|
|
309
|
+
const gpu = !reading.timer
|
|
310
|
+
? "gpu n/a"
|
|
311
|
+
: reading.gpuP75Ms === null
|
|
312
|
+
? "p75 -"
|
|
313
|
+
: `p75 ${reading.gpuP75Ms.toFixed(1)}/${reading.periodMs.toFixed(1)}ms`;
|
|
314
|
+
const late = reading.late === null ? "" : ` late ${reading.late.frames}/${reading.late.of}`;
|
|
315
|
+
const why = reading.reason === null ? "" : ` (${reading.reason})`;
|
|
316
|
+
return `scale ${reading.scale.toFixed(2)}${next} ${reading.mode} ${gpu}${late}${why}`;
|
|
317
|
+
}
|
|
318
|
+
|
|
122
319
|
/** DOM half: the readout. `layers` is passed in because only the caller knows
|
|
123
|
-
* which scene is current; everything else is sampled here.
|
|
124
|
-
|
|
320
|
+
* which scene is current; everything else is sampled here.
|
|
321
|
+
*
|
|
322
|
+
* `quality` and the render scale are printed, and mirrored onto data
|
|
323
|
+
* attributes, so a drop in either is VISIBLE while it happens: a downgrade
|
|
324
|
+
* nobody can see is the kind that gets blamed on the renderer. */
|
|
325
|
+
export function PerfHud({
|
|
326
|
+
box,
|
|
327
|
+
layers,
|
|
328
|
+
telemetry,
|
|
329
|
+
quality,
|
|
330
|
+
renderScale,
|
|
331
|
+
inset,
|
|
332
|
+
}: {
|
|
333
|
+
box: PerfBox;
|
|
334
|
+
layers: number;
|
|
335
|
+
telemetry: StagePerformanceTelemetry;
|
|
336
|
+
quality: PresentationQuality;
|
|
337
|
+
/** The playback render scale's controller, read inside the interval. */
|
|
338
|
+
renderScale: RenderScaleController;
|
|
339
|
+
/** Where the chrome covers the stage; the HUD sits just inside it. */
|
|
340
|
+
inset: StageInset;
|
|
341
|
+
}) {
|
|
125
342
|
const [text, setText] = useState("perf…");
|
|
343
|
+
const [scale, setScale] = useState(1);
|
|
344
|
+
useEffect(() => {
|
|
345
|
+
if (windowPerfProbe === null) return;
|
|
346
|
+
const readStage = (afterSequence = 0): StagePerformanceDebugSnapshot | null =>
|
|
347
|
+
telemetry.debugSnapshot(afterSequence);
|
|
348
|
+
const readStageSequence = (): number | null => telemetry.debugSampleSequence();
|
|
349
|
+
windowPerfProbe.stage = readStage;
|
|
350
|
+
windowPerfProbe.stageSequence = readStageSequence;
|
|
351
|
+
const subscribeStage = (listener: (sample: StagePresentationSample) => void) =>
|
|
352
|
+
telemetry.subscribeDebugSample(listener);
|
|
353
|
+
// Event-shaped diagnostics are useful to pixel gates as well as to the
|
|
354
|
+
// sampling profiler. Publishing the function under the existing `?perf`
|
|
355
|
+
// boundary adds no per-frame work until a caller actually subscribes.
|
|
356
|
+
windowPerfProbe.subscribeStage = subscribeStage;
|
|
357
|
+
const readGpu = (fromMs?: number, toMs?: number): GpuStats & { timer: PerfBox["gpuTimer"] } => ({
|
|
358
|
+
timer: box.gpuTimer,
|
|
359
|
+
...(box.gpu?.stats(fromMs, toMs) ?? NO_GPU_STATS),
|
|
360
|
+
});
|
|
361
|
+
windowPerfProbe.gpu = readGpu;
|
|
362
|
+
return () => {
|
|
363
|
+
if (windowPerfProbe.stage === readStage) delete windowPerfProbe.stage;
|
|
364
|
+
if (windowPerfProbe.stageSequence === readStageSequence) {
|
|
365
|
+
delete windowPerfProbe.stageSequence;
|
|
366
|
+
}
|
|
367
|
+
if (windowPerfProbe.subscribeStage === subscribeStage) delete windowPerfProbe.subscribeStage;
|
|
368
|
+
if (windowPerfProbe.gpu === readGpu) delete windowPerfProbe.gpu;
|
|
369
|
+
};
|
|
370
|
+
}, [box, telemetry]);
|
|
371
|
+
// A ref, not a dependency: the rung changes on its own schedule and must not
|
|
372
|
+
// restart the sampling interval (which would reset every delta baseline).
|
|
373
|
+
const qualityRef = useRef(quality);
|
|
374
|
+
qualityRef.current = quality;
|
|
126
375
|
// rAF gap EMA, sampled continuously but folded into state only per interval.
|
|
127
376
|
const uiMs = useRef(16.7);
|
|
128
377
|
useEffect(() => {
|
|
378
|
+
if (PERF_SELF_PROFILE_ENABLED) return;
|
|
129
379
|
let raf = 0;
|
|
130
380
|
let last = performance.now();
|
|
131
381
|
const tick = (): void => {
|
|
@@ -142,6 +392,7 @@ export function PerfHud({ box, layers }: { box: PerfBox; layers: number }) {
|
|
|
142
392
|
// rAF-gap EMA has already recovered by sample time.
|
|
143
393
|
const longTasks = useRef({ count: 0, maxMs: 0 });
|
|
144
394
|
useEffect(() => {
|
|
395
|
+
if (PERF_SELF_PROFILE_ENABLED) return;
|
|
145
396
|
if (typeof PerformanceObserver === "undefined") return;
|
|
146
397
|
const observer = new PerformanceObserver((list) => {
|
|
147
398
|
for (const entry of list.getEntries()) {
|
|
@@ -157,38 +408,173 @@ export function PerfHud({ box, layers }: { box: PerfBox; layers: number }) {
|
|
|
157
408
|
return () => observer.disconnect();
|
|
158
409
|
}, []);
|
|
159
410
|
useEffect(() => {
|
|
411
|
+
if (PERF_SELF_PROFILE_ENABLED) return;
|
|
160
412
|
let lastFrames = box.glFrames;
|
|
161
413
|
let lastHits = rasterCacheStats().hits;
|
|
162
414
|
let lastMisses = rasterCacheStats().misses;
|
|
415
|
+
let lastPerf = shotPerfStats();
|
|
416
|
+
let lastUploads = canvasUploadStats();
|
|
417
|
+
let lastStage = telemetry.snapshot();
|
|
163
418
|
const timer = setInterval(() => {
|
|
164
419
|
const stats = rasterCacheStats();
|
|
420
|
+
const perf = shotPerfStats();
|
|
421
|
+
const stage = telemetry.snapshot();
|
|
165
422
|
const gps = ((box.glFrames - lastFrames) * 1000) / SAMPLE_MS;
|
|
166
423
|
const hits = stats.hits - lastHits;
|
|
167
424
|
const misses = stats.misses - lastMisses;
|
|
425
|
+
// Per-INTERVAL deltas, so the numbers describe the last half second
|
|
426
|
+
// rather than the session. `passes` is the double-draw detector: GL
|
|
427
|
+
// passes divided by the distinct playhead frames they were drawn for.
|
|
428
|
+
// 1.0 is one draw per frame; 2.0 means every frame is rendered twice,
|
|
429
|
+
// which is what a live video layer used to cost.
|
|
430
|
+
const drawDelta = perf.draws - lastPerf.draws;
|
|
431
|
+
const frameDelta = perf.frames - lastPerf.frames;
|
|
432
|
+
const passes = frameDelta > 0 ? drawDelta / frameDelta : 0;
|
|
433
|
+
// Splits a doubling into its two possible causes, which is the whole
|
|
434
|
+
// reason `sceneRenders` is counted: renders-per-frame above 1 means a
|
|
435
|
+
// second COMMIT upstream, while passes above renders means one render
|
|
436
|
+
// drawing twice. Printing only `passes` left the two indistinguishable,
|
|
437
|
+
// so every judgement about a doubling was made by reading code.
|
|
438
|
+
const renderDelta = perf.sceneRenders - lastPerf.sceneRenders;
|
|
439
|
+
const renders = frameDelta > 0 ? renderDelta / frameDelta : 0;
|
|
440
|
+
// Where those renders CAME FROM. `host` rising with `renders` means the
|
|
441
|
+
// editor pushed the commits down; `host` flat while `renders` climbs
|
|
442
|
+
// means state inside the canvas subtree is doing it.
|
|
443
|
+
const hostDelta = perf.runtimeRenders - lastPerf.runtimeRenders;
|
|
444
|
+
const hostRenders = frameDelta > 0 ? hostDelta / frameDelta : 0;
|
|
445
|
+
const rootDelta = perf.rootRenders - lastPerf.rootRenders;
|
|
446
|
+
const rootRenders = frameDelta > 0 ? rootDelta / frameDelta : 0;
|
|
447
|
+
// TRUE renderer entries per playhead frame, from the call sites rather
|
|
448
|
+
// than from ClockDriver advances. A plain scene is 1; a bin list, a post
|
|
449
|
+
// chain or a transition legitimately draw more, and this is the only
|
|
450
|
+
// number that says how much the GPU was actually asked to do.
|
|
451
|
+
const passDelta = perf.renderPasses - lastPerf.renderPasses;
|
|
452
|
+
const glPasses = frameDelta > 0 ? passDelta / frameDelta : 0;
|
|
453
|
+
const armDelta = perf.mediaArms - lastPerf.mediaArms;
|
|
454
|
+
const repaintDelta = perf.repaints - lastPerf.repaints;
|
|
455
|
+
// Which setter inside the canvas asked for the extra scene render.
|
|
456
|
+
const bumps: Array<[string, number]> = [
|
|
457
|
+
["f", perf.bumpFonts - lastPerf.bumpFonts],
|
|
458
|
+
["h", perf.bumpHtml - lastPerf.bumpHtml],
|
|
459
|
+
["m", perf.bumpMedia - lastPerf.bumpMedia],
|
|
460
|
+
["e", perf.bumpEnv - lastPerf.bumpEnv],
|
|
461
|
+
];
|
|
462
|
+
const bumpText = bumps
|
|
463
|
+
.filter((row) => row[1] > 0)
|
|
464
|
+
.map((row) => `${row[0]}${row[1]}`)
|
|
465
|
+
.join("");
|
|
466
|
+
const compilePer = drawDelta > 0 ? (perf.compileMs - lastPerf.compileMs) / drawDelta : 0;
|
|
467
|
+
const advancePer = drawDelta > 0 ? (perf.advanceMs - lastPerf.advanceMs) / drawDelta : 0;
|
|
468
|
+
const watchPer = drawDelta > 0 ? (perf.watchMs - lastPerf.watchMs) / drawDelta : 0;
|
|
469
|
+
// The runtime subtree's whole render and commit — see `treeMs`. Printed
|
|
470
|
+
// beside `adv` because the pair is the split that matters: a seek is
|
|
471
|
+
// `tree` plus what the host component above it costs, and until this
|
|
472
|
+
// number existed the two were one unattributed remainder.
|
|
473
|
+
const treePer = drawDelta > 0 ? (perf.treeMs - lastPerf.treeMs) / drawDelta : 0;
|
|
474
|
+
// The frame program's two halves per draw, and how the transport's
|
|
475
|
+
// frames split between the clock listener (no commit) and a structure
|
|
476
|
+
// commit (with its whole cost per commit). `tree` above covers only the
|
|
477
|
+
// commits that rendered the runtime; `commit` is the one to read during
|
|
478
|
+
// playback.
|
|
479
|
+
const evalPer = drawDelta > 0 ? (perf.evaluateMs - lastPerf.evaluateMs) / drawDelta : 0;
|
|
480
|
+
const applyPer = drawDelta > 0 ? (perf.applyMs - lastPerf.applyMs) / drawDelta : 0;
|
|
481
|
+
const clockDelta = perf.clockFrames - lastPerf.clockFrames;
|
|
482
|
+
const commitDelta = perf.commitFrames - lastPerf.commitFrames;
|
|
483
|
+
const commitPer = commitDelta > 0 ? (perf.commitMs - lastPerf.commitMs) / commitDelta : 0;
|
|
484
|
+
const clockShare = drawDelta > 0 ? (100 * clockDelta) / drawDelta : 0;
|
|
485
|
+
// Request → publish → present, and everything lost between them. This
|
|
486
|
+
// is the signal the quality policy actually acts on, so it is worth
|
|
487
|
+
// seeing beside the numbers that explain WHY a window was slow.
|
|
488
|
+
//
|
|
489
|
+
// `renderPasses` is the runtime's ClockDriver advances here. The lane
|
|
490
|
+
// this came from counted true renderer entry points instead, which also
|
|
491
|
+
// sees the bins, transition and split presenters; without that counter
|
|
492
|
+
// `effective` reads as advances-per-presentation rather than
|
|
493
|
+
// passes-per-presentation.
|
|
494
|
+
const stageInterval = stagePerformanceInterval(lastStage, stage, drawDelta);
|
|
495
|
+
const stageText = stagePerformanceLine(stage, stageInterval, qualityRef.current);
|
|
496
|
+
// CanvasTexture upload requests: the one cost `gl.info` cannot see. Per
|
|
497
|
+
// PRESENTED frame when frames moved; raw interval totals otherwise,
|
|
498
|
+
// because uploads on a paused stage are themselves the anomaly worth
|
|
499
|
+
// printing (the budget for them is zero — see docs/shot.md).
|
|
500
|
+
const uploadsNow = canvasUploadStats();
|
|
501
|
+
const uploadDelta = uploadsNow.uploads - lastUploads.uploads;
|
|
502
|
+
const uploadMpx = uploadsNow.mpx - lastUploads.mpx;
|
|
503
|
+
lastUploads = uploadsNow;
|
|
504
|
+
const upText =
|
|
505
|
+
uploadDelta > 0
|
|
506
|
+
? frameDelta > 0
|
|
507
|
+
? ` · up ${(uploadDelta / frameDelta).toFixed(1)}/${(uploadMpx / frameDelta).toFixed(1)}MPx`
|
|
508
|
+
: ` · up ${uploadDelta}/${uploadMpx.toFixed(1)}MPx paused`
|
|
509
|
+
: "";
|
|
510
|
+
lastStage = stage;
|
|
168
511
|
lastFrames = box.glFrames;
|
|
169
512
|
lastHits = stats.hits;
|
|
170
513
|
lastMisses = stats.misses;
|
|
514
|
+
lastPerf = perf;
|
|
171
515
|
const lt = longTasks.current;
|
|
172
516
|
const ltText = lt.count > 0 ? ` · lt ${lt.count}/${lt.maxMs.toFixed(0)}ms` : "";
|
|
173
517
|
longTasks.current = { count: 0, maxMs: 0 };
|
|
174
518
|
const spans = Object.entries(box.spans)
|
|
175
519
|
.map(([name, ms]) => ` · ${name} ${ms.toFixed(0)}ms`)
|
|
176
520
|
.join("");
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
521
|
+
// GPU time per frame over the frames resolved since the last sample; a
|
|
522
|
+
// dash when nothing drew.
|
|
523
|
+
const sampledAt = performance.now();
|
|
524
|
+
const gpu = box.gpu?.stats(sampledAt - SAMPLE_MS, sampledAt) ?? NO_GPU_STATS;
|
|
525
|
+
const gpuText =
|
|
526
|
+
box.gpuTimer === "n/a"
|
|
527
|
+
? "n/a"
|
|
528
|
+
: gpu.p50Ms === null || gpu.p95Ms === null
|
|
529
|
+
? "-"
|
|
530
|
+
: `${gpu.p50Ms.toFixed(1)}/${gpu.p95Ms.toFixed(1)}ms`;
|
|
531
|
+
// The render scale, off the controller in this interval: no React state
|
|
532
|
+
// above the runtime moves per window.
|
|
533
|
+
const reading = renderScale.reading();
|
|
534
|
+
// Only while something is actually drawing: a paused stage draws nothing,
|
|
535
|
+
// and printing "passes 0.0 · compile 0.0ms" there would read as a broken
|
|
536
|
+
// renderer rather than an idle one.
|
|
537
|
+
const drawText =
|
|
538
|
+
drawDelta > 0
|
|
539
|
+
? ` · passes ${passes.toFixed(2)} · renders ${renders.toFixed(2)} · host ${hostRenders.toFixed(2)} · root ${rootRenders.toFixed(2)} · gl ${glPasses.toFixed(2)} · arm ${armDelta}/rep ${repaintDelta}${bumpText === "" ? "" : ` · bump ${bumpText}`}${repaintDelta > 0 ? ` (${perf.lastRepaintTrigger})` : ""} · compile ${compilePer.toFixed(1)}ms · adv ${advancePer.toFixed(1)}ms · watch ${watchPer.toFixed(1)}ms · tree ${treePer.toFixed(1)}ms · eval ${evalPer.toFixed(2)}ms · apply ${applyPer.toFixed(2)}ms · clock ${clockShare.toFixed(0)}% · commit ${commitDelta}/${commitPer.toFixed(1)}ms`
|
|
540
|
+
: "";
|
|
541
|
+
const line =
|
|
542
|
+
`gl/s ${gps.toFixed(0)} · ui ${uiMs.current.toFixed(1)}ms · gpu ${gpuText} · draws ${box.drawCalls} · ` +
|
|
543
|
+
`tris ${box.triangles} · progs ${box.programs} · ` +
|
|
544
|
+
`raster ${(stats.bytes / 1e6).toFixed(0)}MB/${stats.entries} (+${hits}h ${misses}m) · ` +
|
|
545
|
+
`planes ${planeCacheStats().entries} · ` +
|
|
546
|
+
`mats ${layerMaterialPoolStats().parked} · ` +
|
|
547
|
+
`tex ${box.textures}/geo ${box.geometries}${upText} · ` +
|
|
548
|
+
`layers ${layers} · ${renderScaleText(reading)} · ${stageText}${drawText}${ltText}${spans}`;
|
|
549
|
+
setText(line);
|
|
550
|
+
setScale(reading.scale);
|
|
551
|
+
// ── AND SOMEWHERE THE DOCK CANNOT COVER ─────────────────────────────
|
|
552
|
+
//
|
|
553
|
+
// The overlay below is inside the stage IFRAME, and `.stage-frame` is
|
|
554
|
+
// `position: fixed; inset: 0`: the iframe fills the whole window and
|
|
555
|
+
// every chrome panel is docked above it. The iframe is one element in the
|
|
556
|
+
// chrome's stacking order, so no z-index in here can beat a docked cell:
|
|
557
|
+
// the HUD sits in the visible canvas's corner, but a long line still
|
|
558
|
+
// runs under the right column. A reading that has to be transcribed off
|
|
559
|
+
// a translucent overlay is also the one thing nobody does accurately.
|
|
560
|
+
//
|
|
561
|
+
// So the line also goes to `window.__perf`, which can be grabbed at any
|
|
562
|
+
// instant from the console, and under `?perf=log` to the console itself
|
|
563
|
+
// for watching a number move over a whole playback.
|
|
564
|
+
(window as unknown as { __perf?: string }).__perf = line;
|
|
565
|
+
if (PERF_LOG_ENABLED) console.log("[perf] " + line);
|
|
183
566
|
}, SAMPLE_MS);
|
|
184
567
|
return () => clearInterval(timer);
|
|
185
|
-
}, [box, layers]);
|
|
568
|
+
}, [box, layers, telemetry, renderScale]);
|
|
569
|
+
if (PERF_SELF_PROFILE_ENABLED) return null;
|
|
186
570
|
return (
|
|
187
571
|
<div
|
|
188
572
|
style={{
|
|
189
573
|
position: "fixed",
|
|
190
|
-
|
|
191
|
-
|
|
574
|
+
// Just inside the docked cells, in the corner of the visible canvas.
|
|
575
|
+
// A literal once put it under the timeline, where it was unreadable.
|
|
576
|
+
left: inset.left + 8,
|
|
577
|
+
top: inset.top + 8,
|
|
192
578
|
zIndex: 40,
|
|
193
579
|
padding: "4px 8px",
|
|
194
580
|
borderRadius: 4,
|
|
@@ -199,6 +585,10 @@ export function PerfHud({ box, layers }: { box: PerfBox; layers: number }) {
|
|
|
199
585
|
whiteSpace: "nowrap",
|
|
200
586
|
}}
|
|
201
587
|
data-testid="perf-hud"
|
|
588
|
+
data-quality-level={quality.level}
|
|
589
|
+
data-quality-dpr={quality.dpr}
|
|
590
|
+
data-quality-raster={quality.rasterScale}
|
|
591
|
+
data-render-scale={scale}
|
|
202
592
|
>
|
|
203
593
|
{text}
|
|
204
594
|
</div>
|