@weasel-js/core 1.9.2 → 1.9.4
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/CHANGELOG.md +393 -0
- package/README.md +2 -2
- package/dist/WeaselProvider.js +5 -5
- package/dist/affordances/composeAffordanceLayer.js +10 -20
- package/dist/affordances/composeAffordanceLayer.js.map +1 -1
- package/dist/affordances/cornerResize.js +25 -17
- package/dist/affordances/cornerResize.js.map +1 -1
- package/dist/affordances/hitAffordanceRegions.js +27 -20
- package/dist/affordances/hitAffordanceRegions.js.map +1 -1
- package/dist/affordances/rotationHandle.js +18 -18
- package/dist/affordances/rotationHandle.js.map +1 -1
- package/dist/animation/behaviors/momentum.js +93 -55
- package/dist/animation/behaviors/momentum.js.map +1 -1
- package/dist/animation/loop.js +10 -12
- package/dist/animation/loop.js.map +1 -1
- package/dist/animation/reflow.js +8 -8
- package/dist/animation/reflow.js.map +1 -1
- package/dist/animation/stagger.js +64 -56
- package/dist/animation/stagger.js.map +1 -1
- package/dist/animation/timeline/booking.js +62 -54
- package/dist/animation/timeline/booking.js.map +1 -1
- package/dist/animation/timeline/createTimeline.js +85 -76
- package/dist/animation/timeline/createTimeline.js.map +1 -1
- package/dist/animation/timeline/sampleTrack.js +13 -5
- package/dist/animation/timeline/sampleTrack.js.map +1 -1
- package/dist/animation/useAnimator.js +45 -35
- package/dist/animation/useAnimator.js.map +1 -1
- package/dist/animation/wrappers/animateLifecycle.js +31 -24
- package/dist/animation/wrappers/animateLifecycle.js.map +1 -1
- package/dist/animation/wrappers/animateOnSetPose.js +28 -26
- package/dist/animation/wrappers/animateOnSetPose.js.map +1 -1
- package/dist/animation/wrappers/instantPose.js +13 -0
- package/dist/animation/wrappers/instantPose.js.map +1 -0
- package/dist/{poseDescriptor-D-OH-tu5.d.ts → autoPoseDescriptor-Ct7yqivU.d.ts} +28 -3
- package/dist/barrel/chrome.js +16 -0
- package/dist/barrel/input.js +19 -0
- package/dist/barrel/interactions.js +42 -0
- package/dist/barrel/paint.js +18 -0
- package/dist/barrel/paths.js +19 -0
- package/dist/barrel/renderer.js +20 -0
- package/dist/barrel/routing.js +51 -0
- package/dist/barrel/text.js +18 -0
- package/dist/barrel/viewport.js +52 -0
- package/dist/builtins-DPcdghtB.d.ts +1223 -0
- package/dist/canvas/Canvas.js +363 -448
- package/dist/canvas/Canvas.js.map +1 -1
- package/dist/canvas/CanvasView.js +164 -165
- package/dist/canvas/CanvasView.js.map +1 -1
- package/dist/canvas/CursorCoordsHud.js +37 -32
- package/dist/canvas/CursorCoordsHud.js.map +1 -1
- package/dist/canvas/DrawCanvas.js +49 -43
- package/dist/canvas/DrawCanvas.js.map +1 -1
- package/dist/canvas/MinimapCanvas.js +26 -25
- package/dist/canvas/MinimapCanvas.js.map +1 -1
- package/dist/canvas/NodeShape.js +59 -377
- package/dist/canvas/NodeShape.js.map +1 -1
- package/dist/canvas/PickHud.js +50 -47
- package/dist/canvas/PickHud.js.map +1 -1
- package/dist/canvas/SceneCanvas/GestureDispatcherMounter.js +167 -0
- package/dist/canvas/SceneCanvas/GestureDispatcherMounter.js.map +1 -0
- package/dist/canvas/SceneCanvas/PointerProviderIfRoot.js +13 -13
- package/dist/canvas/SceneCanvas/StandardActionsRegistrar.js +67 -0
- package/dist/canvas/SceneCanvas/StandardActionsRegistrar.js.map +1 -0
- package/dist/canvas/SceneCanvas/ToolKeybindingsMounter.js +16 -0
- package/dist/canvas/SceneCanvas/ToolKeybindingsMounter.js.map +1 -0
- package/dist/canvas/SceneCanvas/coordTrace.js +10 -0
- package/dist/canvas/SceneCanvas/coordTrace.js.map +1 -0
- package/dist/canvas/SceneCanvas/defaultNodeProperties.js +146 -92
- package/dist/canvas/SceneCanvas/defaultNodeProperties.js.map +1 -1
- package/dist/canvas/SceneCanvas/defaultNodeRouting.js +9 -8
- package/dist/canvas/SceneCanvas/defaultNodeRouting.js.map +1 -1
- package/dist/canvas/SceneCanvas/features.js +1 -0
- package/dist/canvas/SceneCanvas/features.js.map +1 -1
- package/dist/canvas/SceneCanvas/isDev.js +12 -0
- package/dist/canvas/SceneCanvas/isDev.js.map +1 -0
- package/dist/canvas/SceneCanvas/poseGeometry.js +29 -28
- package/dist/canvas/SceneCanvas/poseGeometry.js.map +1 -1
- package/dist/canvas/SceneCanvas/poseRegistrars.js +13 -0
- package/dist/canvas/SceneCanvas/poseRegistrars.js.map +1 -0
- package/dist/canvas/SceneCanvas/props.js +38 -0
- package/dist/canvas/SceneCanvas/props.js.map +1 -0
- package/dist/canvas/SceneCanvas/useBuiltinShapeTools.js +21 -62
- package/dist/canvas/SceneCanvas/useBuiltinShapeTools.js.map +1 -1
- package/dist/canvas/SceneCanvas/useDispatcherOverlayLayer.js.map +1 -1
- package/dist/canvas/SceneCanvas/usePreviewGhostLayer.js +46 -48
- package/dist/canvas/SceneCanvas/usePreviewGhostLayer.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneSelectTool.js +33 -33
- package/dist/canvas/SceneCanvas/useSceneSelectTool.js.map +1 -1
- package/dist/canvas/SceneCanvas.js +534 -788
- package/dist/canvas/SceneCanvas.js.map +1 -1
- package/dist/canvas/SceneViewCanvas.js +45 -43
- package/dist/canvas/SceneViewCanvas.js.map +1 -1
- package/dist/canvas/buildSceneLayer.js +91 -0
- package/dist/canvas/buildSceneLayer.js.map +1 -0
- package/dist/canvas/buildSceneTree.js +16 -19
- package/dist/canvas/buildSceneTree.js.map +1 -1
- package/dist/canvas/canvasRenderer.js +68 -52
- package/dist/canvas/canvasRenderer.js.map +1 -1
- package/dist/canvas/deps/changes.js +13 -0
- package/dist/canvas/deps/changes.js.map +1 -0
- package/dist/canvas/deps/editAnchors.js +28 -28
- package/dist/canvas/deps/insert.js +119 -91
- package/dist/canvas/deps/insert.js.map +1 -1
- package/dist/canvas/deps/slice.js +46 -45
- package/dist/canvas/deps/slice.js.map +1 -1
- package/dist/canvas/deps/textEdit.js +10 -9
- package/dist/canvas/deps/textEdit.js.map +1 -1
- package/dist/canvas/derivedPath.js +46 -23
- package/dist/canvas/derivedPath.js.map +1 -1
- package/dist/canvas/getNodeAtPoint.js +7 -7
- package/dist/canvas/getNodeAtPoint.js.map +1 -1
- package/dist/canvas/hud.module.js +1 -1
- package/dist/canvas/hud.module.js.map +1 -1
- package/dist/canvas/kitRequirements.js +2 -2
- package/dist/canvas/kitRequirements.js.map +1 -1
- package/dist/canvas/layerOrder.js +13 -6
- package/dist/canvas/layerOrder.js.map +1 -1
- package/dist/canvas/minimapMath.js +12 -10
- package/dist/canvas/minimapMath.js.map +1 -1
- package/dist/canvas/nodeShapes/derivedPainter.js +34 -0
- package/dist/canvas/nodeShapes/derivedPainter.js.map +1 -0
- package/dist/canvas/nodeShapes/imagePainter.js +87 -0
- package/dist/canvas/nodeShapes/imagePainter.js.map +1 -0
- package/dist/canvas/nodeShapes/memo.js +9 -0
- package/dist/canvas/nodeShapes/memo.js.map +1 -0
- package/dist/canvas/nodeShapes/nodePaint.js +57 -0
- package/dist/canvas/nodeShapes/nodePaint.js.map +1 -0
- package/dist/canvas/nodeShapes/pathPainter.js +38 -0
- package/dist/canvas/nodeShapes/pathPainter.js.map +1 -0
- package/dist/canvas/nodeShapes/poseBox.js +20 -0
- package/dist/canvas/nodeShapes/poseBox.js.map +1 -0
- package/dist/canvas/nodeShapes/rectFallbackPainter.js +38 -0
- package/dist/canvas/nodeShapes/rectFallbackPainter.js.map +1 -0
- package/dist/canvas/nodeShapes/shapePainter.js +72 -0
- package/dist/canvas/nodeShapes/shapePainter.js.map +1 -0
- package/dist/canvas/nodeShapes/textPainter.js +55 -0
- package/dist/canvas/nodeShapes/textPainter.js.map +1 -0
- package/dist/canvas/overlayRoleStrokes.js.map +1 -1
- package/dist/canvas/paintCull.js +10 -11
- package/dist/canvas/paintCull.js.map +1 -1
- package/dist/canvas/pickWalk.js +52 -63
- package/dist/canvas/pickWalk.js.map +1 -1
- package/dist/canvas/poseRotation.js +11 -23
- package/dist/canvas/poseRotation.js.map +1 -1
- package/dist/canvas/renderDebugSnapshot.js +5 -5
- package/dist/canvas/renderSceneToPixels.js +3 -1
- package/dist/canvas/renderSceneToPixels.js.map +1 -1
- package/dist/canvas/sceneAdapter.js +66 -148
- package/dist/canvas/sceneAdapter.js.map +1 -1
- package/dist/canvas/sceneClipboard.js +112 -0
- package/dist/canvas/sceneClipboard.js.map +1 -0
- package/dist/canvas/sceneSlotWiring.js +4 -4
- package/dist/canvas/sceneSlotWiring.js.map +1 -1
- package/dist/canvas/sceneViewRender.js +39 -40
- package/dist/canvas/sceneViewRender.js.map +1 -1
- package/dist/canvas/useCanvasRenderer.js +14 -6
- package/dist/canvas/useCanvasRenderer.js.map +1 -1
- package/dist/canvas/useCanvasView.js +58 -0
- package/dist/canvas/useCanvasView.js.map +1 -0
- package/dist/canvas/useFrameLoop.js +35 -26
- package/dist/canvas/useFrameLoop.js.map +1 -1
- package/dist/canvas/useSceneLayerList.js +21 -0
- package/dist/canvas/useSceneLayerList.js.map +1 -0
- package/dist/clipboard.d.ts +10 -6
- package/dist/clone.d.ts +4 -3
- package/dist/core/adapters/arrayAdapter.js +48 -39
- package/dist/core/adapters/arrayAdapter.js.map +1 -1
- package/dist/core/device/index.js +5 -4
- package/dist/core/device/useDeviceProfile.js +38 -44
- package/dist/core/device/useDeviceProfile.js.map +1 -1
- package/dist/core/device/watchDevicePixelRatio.js +17 -0
- package/dist/core/device/watchDevicePixelRatio.js.map +1 -0
- package/dist/core/fillInPoseFrame.js.map +1 -1
- package/dist/core/geometry/poseDescriptor.js +21 -19
- package/dist/core/geometry/poseDescriptor.js.map +1 -1
- package/dist/core/geometry/poseFrame.js +44 -0
- package/dist/core/geometry/poseFrame.js.map +1 -0
- package/dist/core/geometry/poseRotation.js.map +1 -1
- package/dist/core/geometry/regionHit.js +9 -10
- package/dist/core/geometry/regionHit.js.map +1 -1
- package/dist/core/geometry/unionBounds.js +31 -29
- package/dist/core/geometry/unionBounds.js.map +1 -1
- package/dist/core/isPlainObject.js +2 -11
- package/dist/core/ops/arrange.js +1 -0
- package/dist/core/ops/arrange.js.map +1 -1
- package/dist/core/ops/create.js +10 -5
- package/dist/core/ops/create.js.map +1 -1
- package/dist/core/ops/delete.js +12 -24
- package/dist/core/ops/delete.js.map +1 -1
- package/dist/core/ops/reorder/index.js +43 -40
- package/dist/core/ops/reorder/index.js.map +1 -1
- package/dist/core/ops/reorder/setChildOrder.js +34 -0
- package/dist/core/ops/reorder/setChildOrder.js.map +1 -0
- package/dist/core/paintKinds.js +5 -5
- package/dist/core/paintKinds.js.map +1 -1
- package/dist/core/scene/NodeProperties.js.map +1 -1
- package/dist/core/scene/appliedLog.js +37 -0
- package/dist/core/scene/appliedLog.js.map +1 -0
- package/dist/core/scene/arrivalWindow.js +101 -0
- package/dist/core/scene/arrivalWindow.js.map +1 -0
- package/dist/core/scene/derivedPath.js.map +1 -1
- package/dist/core/scene/historyFacade.js +84 -0
- package/dist/core/scene/historyFacade.js.map +1 -0
- package/dist/core/scene/kitOps.js +173 -0
- package/dist/core/scene/kitOps.js.map +1 -0
- package/dist/core/scene/layerStack.js +38 -0
- package/dist/core/scene/layerStack.js.map +1 -0
- package/dist/core/scene/lockGuard.js +37 -0
- package/dist/core/scene/lockGuard.js.map +1 -0
- package/dist/core/scene/nodeFnFields.js +5 -6
- package/dist/core/scene/nodeFnFields.js.map +1 -1
- package/dist/core/scene/nodeMemo.js +45 -19
- package/dist/core/scene/nodeMemo.js.map +1 -1
- package/dist/core/scene/nodeStore.js +126 -0
- package/dist/core/scene/nodeStore.js.map +1 -0
- package/dist/core/scene/pickability.js +6 -0
- package/dist/core/scene/pickability.js.map +1 -0
- package/dist/core/scene/poseFeed.js +38 -38
- package/dist/core/scene/poseFeed.js.map +1 -1
- package/dist/core/scene/poseOverrides.js +34 -38
- package/dist/core/scene/poseOverrides.js.map +1 -1
- package/dist/core/scene/renderOrder.js +84 -0
- package/dist/core/scene/renderOrder.js.map +1 -0
- package/dist/core/scene/scene.js +289 -866
- package/dist/core/scene/scene.js.map +1 -1
- package/dist/core/scene/serialization.js +68 -0
- package/dist/core/scene/serialization.js.map +1 -0
- package/dist/core/scene/treeOrder.js +13 -0
- package/dist/core/scene/treeOrder.js.map +1 -0
- package/dist/core/scene/types.js.map +1 -1
- package/dist/core/viewport/meanScale.js +1 -1
- package/dist/core/viewport/meanScale.js.map +1 -1
- package/dist/core/viewport/view.js +12 -12
- package/dist/core/viewport/view.js.map +1 -1
- package/dist/core/viewport/zoomBounds.js +10 -8
- package/dist/core/viewport/zoomBounds.js.map +1 -1
- package/dist/{cornerHandles-BCrDp_mH.d.ts → cornerHandles-CtCb8a3G.d.ts} +1 -1
- package/dist/debug/createDebugOverlayLayer.js +14 -14
- package/dist/debug/createDebugOverlayLayer.js.map +1 -1
- package/dist/debug/flag.js +2 -41
- package/dist/features/chrome-caps/useHoverTracking.js +22 -17
- package/dist/features/chrome-caps/useHoverTracking.js.map +1 -1
- package/dist/features/focus/gateLayer.js +3 -4
- package/dist/features/focus/gateLayer.js.map +1 -1
- package/dist/features/images/imageCache.js +19 -9
- package/dist/features/images/imageCache.js.map +1 -1
- package/dist/features/ingestion/contentHandlers.js +14 -11
- package/dist/features/ingestion/contentHandlers.js.map +1 -1
- package/dist/features/ingestion/index.js +1 -1
- package/dist/features/ingestion/registerKitHandlers.js +7 -7
- package/dist/features/ingestion/weaselJsonHandler.js +17 -14
- package/dist/features/ingestion/weaselJsonHandler.js.map +1 -1
- package/dist/features/meshPaint/lazyKind.js +201 -35
- package/dist/features/meshPaint/lazyKind.js.map +1 -1
- package/dist/features/meshPaint/meshPaint.js +82 -79
- package/dist/features/meshPaint/meshPaint.js.map +1 -1
- package/dist/features/minimap/createMinimapContribution.js +42 -42
- package/dist/features/minimap/layers.js +44 -62
- package/dist/features/minimap/layers.js.map +1 -1
- package/dist/features/parallax/createParallaxPlane.js +10 -14
- package/dist/features/parallax/createParallaxPlane.js.map +1 -1
- package/dist/features/paths/anchorEdits.js +19 -8
- package/dist/features/paths/anchorEdits.js.map +1 -1
- package/dist/features/paths/anchors.js +31 -26
- package/dist/features/paths/anchors.js.map +1 -1
- package/dist/features/paths/booleans.js +2 -2
- package/dist/features/paths/index.js +13 -11
- package/dist/features/paths/markerCommands.js +37 -36
- package/dist/features/paths/markerCommands.js.map +1 -1
- package/dist/features/paths/pathInWorld.js +19 -19
- package/dist/features/paths/pathInWorld.js.map +1 -1
- package/dist/features/paths/pathLayer.js.map +1 -1
- package/dist/features/paths/strokeToPath.js +40 -0
- package/dist/features/paths/strokeToPath.js.map +1 -0
- package/dist/features/paths/tessellate/stroke.js.map +1 -1
- package/dist/features/patterns/commandPaint.js +18 -0
- package/dist/features/patterns/commandPaint.js.map +1 -0
- package/dist/features/patterns/index.js +5 -17
- package/dist/features/patterns/index.js.map +1 -1
- package/dist/features/patterns/rasterizeTile.js +17 -0
- package/dist/features/patterns/rasterizeTile.js.map +1 -0
- package/dist/features/patterns/resolveSpec.js +52 -44
- package/dist/features/patterns/resolveSpec.js.map +1 -1
- package/dist/features/poseRun/usePoseRun.js +8 -3
- package/dist/features/poseRun/usePoseRun.js.map +1 -1
- package/dist/features/selection/SelectionContext.js +5 -0
- package/dist/features/selection/SelectionContext.js.map +1 -1
- package/dist/features/selection/overlay.js +18 -18
- package/dist/features/selection/overlay.js.map +1 -1
- package/dist/features/text/domOffsets.js +82 -0
- package/dist/features/text/domOffsets.js.map +1 -0
- package/dist/features/text/domRuns.js +103 -131
- package/dist/features/text/domRuns.js.map +1 -1
- package/dist/features/text/hitTest.js +5 -2
- package/dist/features/text/hitTest.js.map +1 -1
- package/dist/features/text/isTextData.js +8 -0
- package/dist/features/text/isTextData.js.map +1 -0
- package/dist/features/text/overlayDirection.js +15 -0
- package/dist/features/text/overlayDirection.js.map +1 -0
- package/dist/features/text/runs/rangeStyle.js +32 -34
- package/dist/features/text/runs/rangeStyle.js.map +1 -1
- package/dist/features/text/textCommand.js +34 -33
- package/dist/features/text/textCommand.js.map +1 -1
- package/dist/features/text/textToPath.js +40 -40
- package/dist/features/text/textToPath.js.map +1 -1
- package/dist/features/text/useSceneTextEdit.js +76 -82
- package/dist/features/text/useSceneTextEdit.js.map +1 -1
- package/dist/features/text/useTextEdit.js +184 -160
- package/dist/features/text/useTextEdit.js.map +1 -1
- package/dist/features/tiling/createTiledLayer.js +24 -22
- package/dist/features/tiling/createTiledLayer.js.map +1 -1
- package/dist/index.d.ts +11010 -7700
- package/dist/index.js +316 -309
- package/dist/input/keyState.js +42 -43
- package/dist/input/keyState.js.map +1 -1
- package/dist/input/useKeyModifiers.js +11 -0
- package/dist/input/useKeyModifiers.js.map +1 -0
- package/dist/interactions/actions/align/align.js +8 -8
- package/dist/interactions/actions/clipboard/clipboardOps.js +34 -32
- package/dist/interactions/actions/clipboard/clipboardOps.js.map +1 -1
- package/dist/interactions/actions/clipboard/materializePaste.js +10 -7
- package/dist/interactions/actions/clipboard/materializePaste.js.map +1 -1
- package/dist/interactions/actions/clipboard/wireFormat.js.map +1 -1
- package/dist/interactions/actions/clone/behaviors/cloneByAltDrag.js.map +1 -1
- package/dist/interactions/actions/defaultCommitAdapter.js +20 -18
- package/dist/interactions/actions/defaultCommitAdapter.js.map +1 -1
- package/dist/interactions/actions/defaults/align.js +9 -10
- package/dist/interactions/actions/defaults/align.js.map +1 -1
- package/dist/interactions/actions/defaults/anchorEditing.js +59 -81
- package/dist/interactions/actions/defaults/anchorEditing.js.map +1 -1
- package/dist/interactions/actions/defaults/areaSelect.js +35 -38
- package/dist/interactions/actions/defaults/areaSelect.js.map +1 -1
- package/dist/interactions/actions/defaults/clipboard.js +26 -10
- package/dist/interactions/actions/defaults/clipboard.js.map +1 -1
- package/dist/interactions/actions/defaults/clone.js +44 -60
- package/dist/interactions/actions/defaults/clone.js.map +1 -1
- package/dist/interactions/actions/defaults/copySubtree.js +40 -0
- package/dist/interactions/actions/defaults/copySubtree.js.map +1 -0
- package/dist/interactions/actions/defaults/distribute.js +14 -13
- package/dist/interactions/actions/defaults/distribute.js.map +1 -1
- package/dist/interactions/actions/defaults/duplicate.js +17 -57
- package/dist/interactions/actions/defaults/duplicate.js.map +1 -1
- package/dist/interactions/actions/defaults/editAnchors.js +18 -18
- package/dist/interactions/actions/defaults/enterTextEdit.js.map +1 -1
- package/dist/interactions/actions/defaults/flip.js +52 -46
- package/dist/interactions/actions/defaults/flip.js.map +1 -1
- package/dist/interactions/actions/defaults/group.js +43 -48
- package/dist/interactions/actions/defaults/group.js.map +1 -1
- package/dist/interactions/actions/defaults/ingest.js +35 -33
- package/dist/interactions/actions/defaults/ingest.js.map +1 -1
- package/dist/interactions/actions/defaults/insert.js +50 -51
- package/dist/interactions/actions/defaults/insert.js.map +1 -1
- package/dist/interactions/actions/defaults/insertPathAnchor.js +7 -7
- package/dist/interactions/actions/defaults/lassoSelect.js +67 -70
- package/dist/interactions/actions/defaults/lassoSelect.js.map +1 -1
- package/dist/interactions/actions/defaults/move.js +267 -249
- package/dist/interactions/actions/defaults/move.js.map +1 -1
- package/dist/interactions/actions/defaults/nudge.js +40 -68
- package/dist/interactions/actions/defaults/nudge.js.map +1 -1
- package/dist/interactions/actions/defaults/nudgeSteps.js +37 -0
- package/dist/interactions/actions/defaults/nudgeSteps.js.map +1 -0
- package/dist/interactions/actions/defaults/reorder.js +2 -0
- package/dist/interactions/actions/defaults/reorder.js.map +1 -1
- package/dist/interactions/actions/defaults/resize.js +104 -98
- package/dist/interactions/actions/defaults/resize.js.map +1 -1
- package/dist/interactions/actions/defaults/rotate.js +111 -110
- package/dist/interactions/actions/defaults/rotate.js.map +1 -1
- package/dist/interactions/actions/defaults/setFill.js +7 -7
- package/dist/interactions/actions/defaults/setFillOpacity.js +4 -4
- package/dist/interactions/actions/defaults/setStroke.js +12 -12
- package/dist/interactions/actions/defaults/setStrokeOpacity.js +6 -6
- package/dist/interactions/actions/defaults/sliceOps.js +19 -19
- package/dist/interactions/actions/defaults/viewportDragPan.js +37 -26
- package/dist/interactions/actions/defaults/viewportDragPan.js.map +1 -1
- package/dist/interactions/actions/edit-anchors/geometry.js +28 -23
- package/dist/interactions/actions/edit-anchors/geometry.js.map +1 -1
- package/dist/interactions/actions/flip/helpers.js +30 -17
- package/dist/interactions/actions/flip/helpers.js.map +1 -1
- package/dist/interactions/actions/hitSelection.js +8 -0
- package/dist/interactions/actions/hitSelection.js.map +1 -0
- package/dist/interactions/actions/lasso-select/behaviors/selectFromLasso.js +14 -18
- package/dist/interactions/actions/lasso-select/behaviors/selectFromLasso.js.map +1 -1
- package/dist/interactions/actions/move/behaviors/snapBackOrDelete.js +5 -5
- package/dist/interactions/actions/move/behaviors/snapBackOrDelete.js.map +1 -1
- package/dist/interactions/actions/move/behaviors/snapToContainer.js +32 -21
- package/dist/interactions/actions/move/behaviors/snapToContainer.js.map +1 -1
- package/dist/interactions/actions/move/gestureAdapter.js +32 -15
- package/dist/interactions/actions/move/gestureAdapter.js.map +1 -1
- package/dist/interactions/actions/move/landedDescendants.js +33 -0
- package/dist/interactions/actions/move/landedDescendants.js.map +1 -0
- package/dist/interactions/actions/outlines/createOutlines.js +18 -18
- package/dist/interactions/actions/poseDescriptorDep.js +16 -4
- package/dist/interactions/actions/poseDescriptorDep.js.map +1 -1
- package/dist/interactions/actions/poseFrame.js +11 -6
- package/dist/interactions/actions/poseFrame.js.map +1 -1
- package/dist/interactions/actions/previewOverrides.js +5 -5
- package/dist/interactions/actions/previewOverrides.js.map +1 -1
- package/dist/interactions/actions/selectionSubtree.js +69 -0
- package/dist/interactions/actions/selectionSubtree.js.map +1 -0
- package/dist/interactions/actions/useStandardActions.js +66 -62
- package/dist/interactions/actions/useStandardActions.js.map +1 -1
- package/dist/interactions/gestures/dragGesture/dragGesture.js +4 -7
- package/dist/interactions/gestures/dragGesture/dragGesture.js.map +1 -1
- package/dist/interactions/gestures/dragGesture/dragPointOf.js +23 -0
- package/dist/interactions/gestures/dragGesture/dragPointOf.js.map +1 -0
- package/dist/interactions/gestures/handleDrag/handleDrag.js +21 -26
- package/dist/interactions/gestures/handleDrag/handleDrag.js.map +1 -1
- package/dist/interactions/gestures/pointerDrag/pointerDrag.js +8 -6
- package/dist/interactions/gestures/pointerDrag/pointerDrag.js.map +1 -1
- package/dist/interactions/gestures/thresholdDrag/thresholdDrag.js +8 -4
- package/dist/interactions/gestures/thresholdDrag/thresholdDrag.js.map +1 -1
- package/dist/{math-Drrw4ewZ.d.ts → math-J-ACHyEg.d.ts} +1 -2
- package/dist/math.d.ts +2 -3
- package/dist/move.d.ts +12 -11
- package/dist/{originProjection-DEklpDc_.d.ts → originProjection-gHnOup4e.d.ts} +1 -1
- package/dist/{registerProgram-BMZRDgrh.d.ts → registerProgram-T9S_PfAX.d.ts} +18 -7
- package/dist/renderer/DrawCommand.js.map +1 -1
- package/dist/renderer/WeaselRenderer.js +75 -73
- package/dist/renderer/WeaselRenderer.js.map +1 -1
- package/dist/renderer/cache/GLImageCache.js +15 -5
- package/dist/renderer/cache/GLImageCache.js.map +1 -1
- package/dist/renderer/cache/GLMeshCache.js +23 -23
- package/dist/renderer/cache/GLMeshCache.js.map +1 -1
- package/dist/renderer/cache/GLTextureCache.js +18 -1
- package/dist/renderer/cache/GLTextureCache.js.map +1 -1
- package/dist/renderer/cache/cache.js.map +1 -1
- package/dist/renderer/cache/markerCommandCache.js.map +1 -1
- package/dist/renderer/cache/strokeMeshCache.js.map +1 -1
- package/dist/renderer/cullDrawCommands.js +11 -11
- package/dist/renderer/draw/batch.js +133 -0
- package/dist/renderer/draw/batch.js.map +1 -0
- package/dist/renderer/draw/clip.js +89 -0
- package/dist/renderer/draw/clip.js.map +1 -0
- package/dist/renderer/draw/customShader.js +230 -0
- package/dist/renderer/draw/customShader.js.map +1 -0
- package/dist/renderer/draw/drawTriangles.js +8 -0
- package/dist/renderer/draw/drawTriangles.js.map +1 -0
- package/dist/renderer/draw/effects.js +28 -0
- package/dist/renderer/draw/effects.js.map +1 -0
- package/dist/renderer/draw/flushBatch.js +35 -0
- package/dist/renderer/draw/flushBatch.js.map +1 -0
- package/dist/renderer/draw/gradient.js +90 -0
- package/dist/renderer/draw/gradient.js.map +1 -0
- package/dist/renderer/draw/image.js +38 -0
- package/dist/renderer/draw/image.js.map +1 -0
- package/dist/renderer/draw/mesh.js +16 -0
- package/dist/renderer/draw/mesh.js.map +1 -0
- package/dist/renderer/draw/paintBind.js +70 -0
- package/dist/renderer/draw/paintBind.js.map +1 -0
- package/dist/renderer/draw/pathPaint.js +142 -0
- package/dist/renderer/draw/pathPaint.js.map +1 -0
- package/dist/renderer/draw/scratchPass.js +83 -0
- package/dist/renderer/draw/scratchPass.js.map +1 -0
- package/dist/renderer/draw/stencil.js +28 -0
- package/dist/renderer/draw/stencil.js.map +1 -0
- package/dist/renderer/draw/text.js +167 -0
- package/dist/renderer/draw/text.js.map +1 -0
- package/dist/renderer/draw/uniforms.js +77 -0
- package/dist/renderer/draw/uniforms.js.map +1 -0
- package/dist/renderer/draw.js +60 -1116
- package/dist/renderer/draw.js.map +1 -1
- package/dist/renderer/effects/EffectTargets.js +3 -0
- package/dist/renderer/effects/EffectTargets.js.map +1 -1
- package/dist/renderer/effects/composite.js +7 -5
- package/dist/renderer/effects/composite.js.map +1 -1
- package/dist/renderer/index.js +18 -17
- package/dist/renderer/math/color.js +63 -83
- package/dist/renderer/math/color.js.map +1 -1
- package/dist/renderer/math/mat3.js +15 -11
- package/dist/renderer/math/mat3.js.map +1 -1
- package/dist/renderer/math/viewToMat3.js +1 -1
- package/dist/renderer/math/viewToMat3.js.map +1 -1
- package/dist/renderer/shaders/ShaderProgram.js +13 -13
- package/dist/renderer/shaders/ShaderProgram.js.map +1 -1
- package/dist/renderer/shaders/glyphPaint.js +3 -3
- package/dist/renderer/shaders/glyphPaint.js.map +1 -1
- package/dist/renderer/shaders/gradFill.js +2 -1
- package/dist/renderer/shaders/gradFill.js.map +1 -1
- package/dist/renderer/shaders/pathFill.js +2 -1
- package/dist/renderer/shaders/pathFill.js.map +1 -1
- package/dist/renderer/shaders/patternFill.js +2 -1
- package/dist/renderer/shaders/patternFill.js.map +1 -1
- package/dist/renderer/shaders/registerProgram.js.map +1 -1
- package/dist/renderer/strokeLengths.js.map +1 -1
- package/dist/renderer/textures/registerTexture.js +25 -5
- package/dist/renderer/textures/registerTexture.js.map +1 -1
- package/dist/renderer.d.ts +16 -15
- package/dist/renderer.js +17 -16
- package/dist/resize.d.ts +5 -6
- package/dist/resize.js +5 -5
- package/dist/style.css +1 -1
- package/dist/test-hook/install.js +12 -6
- package/dist/test-hook/install.js.map +1 -1
- package/dist/{test-seams-CuIxe0xY.d.ts → test-seams-DM2B87-g.d.ts} +4 -4
- package/dist/test-seams.d.ts +2 -2
- package/dist/tools/builtin/ellipse/useEllipseTool.js +1 -0
- package/dist/tools/builtin/ellipse/useEllipseTool.js.map +1 -1
- package/dist/tools/builtin/hand/useHandTool.js +1 -0
- package/dist/tools/builtin/hand/useHandTool.js.map +1 -1
- package/dist/tools/builtin/lasso/useLassoTool.js +11 -11
- package/dist/tools/builtin/lasso/useLassoTool.js.map +1 -1
- package/dist/tools/builtin/line/useLineTool.js +1 -0
- package/dist/tools/builtin/line/useLineTool.js.map +1 -1
- package/dist/tools/builtin/pen/usePenTool.js +153 -103
- package/dist/tools/builtin/pen/usePenTool.js.map +1 -1
- package/dist/tools/builtin/pencil/usePencilTool.js +1 -0
- package/dist/tools/builtin/pencil/usePencilTool.js.map +1 -1
- package/dist/tools/builtin/polygon/usePolygonTool.js +13 -8
- package/dist/tools/builtin/polygon/usePolygonTool.js.map +1 -1
- package/dist/tools/builtin/rect/useRectTool.js +1 -0
- package/dist/tools/builtin/rect/useRectTool.js.map +1 -1
- package/dist/tools/builtin/select/selectionContributions.js +24 -23
- package/dist/tools/builtin/select/selectionContributions.js.map +1 -1
- package/dist/tools/builtin/select/useSelectTool.js +51 -48
- package/dist/tools/builtin/select/useSelectTool.js.map +1 -1
- package/dist/tools/builtin/shared/pointerFollower.js +17 -0
- package/dist/tools/builtin/shared/pointerFollower.js.map +1 -0
- package/dist/tools/builtin/slice/useSliceTool.js +49 -54
- package/dist/tools/builtin/slice/useSliceTool.js.map +1 -1
- package/dist/tools/builtin/star/useStarTool.js +22 -16
- package/dist/tools/builtin/star/useStarTool.js.map +1 -1
- package/dist/tools/builtin/text/useTextTool.js +13 -2
- package/dist/tools/builtin/text/useTextTool.js.map +1 -1
- package/dist/tools/useKeybindings.js +14 -30
- package/dist/tools/useKeybindings.js.map +1 -1
- package/dist/{types-B_9g6hMM.d.ts → types-B4iJRHC3.d.ts} +5 -1
- package/dist/{types-A37hYkeR.d.ts → types-k-5Oqh-U.d.ts} +31 -7
- package/dist/util/color.js +3 -3
- package/dist/util/color.js.map +1 -1
- package/dist/util/paint.js.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +20 -18
- package/dist/autoPoseDescriptor-vCdt55xm.d.ts +0 -27
- package/dist/builtins-C-VgUlWI.d.ts +0 -4514
- package/dist/core/isPlainObject.js.map +0 -1
- package/dist/debug/flag.js.map +0 -1
- package/dist/interactions/actions/defaults/freshNodeId.js +0 -10
- package/dist/interactions/actions/defaults/freshNodeId.js.map +0 -1
- package/dist/renderer/shaders/imageFill.js +0 -14
- package/dist/renderer/shaders/imageFill.js.map +0 -1
- package/dist/tools/prefs.js +0 -47
- package/dist/tools/prefs.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WeaselRenderer.js","names":["customUniformNames","CUSTOM_KIT_UNIFORMS","extractUniformNames","vertSrc","fragSrc","compileCustomProgram","src","CUSTOM_VERT_SRC","program","ShaderProgram","gl","extractUniformArrays","CUSTOM_ATTRIBUTES","now","GLYPH_PAINT_SOURCES","GLYPH_PATTERN_FRAG_SRC","GLYPH_PATTERN_UNIFORMS","GLYPH_GRAD_FRAG_SRC","GLYPH_GRAD_UNIFORMS","GLYPH_TEXTURE_FRAG_SRC","GLYPH_TEXTURE_UNIFORMS","WeaselRenderer","GroupState","createPaintResources","opts","EffectTargets","PaintScratch","attrs","DEFAULT_BAKE_BUDGET","OUTLINE_MIN_SCREEN_PX","VERT_SRC","FRAG_SRC","PATH_FILL_UNIFORMS","PATH_FILL_ATTRIBUTES","VCOLOR_VERT_SRC","VCOLOR_FRAG_SRC","PATH_FILL_VCOLOR_ATTRIBUTES","IMAGE_VERT_SRC","IMAGE_FRAG_SRC","IMAGE_FILL_UNIFORMS","IMAGE_FILL_ATTRIBUTES","BATCH_VERT_SRC","BATCH_FRAG_SRC","BATCH_FILL_UNIFORMS","BATCH_FILL_ATTRIBUTES","GRAD_VERT_SRC","GRAD_FRAG_SRC","GRAD_FILL_UNIFORMS","GRAD_FILL_ATTRIBUTES","PATTERN_VERT_SRC","PATTERN_FRAG_SRC","PATTERN_FILL_UNIFORMS","PATTERN_FILL_ATTRIBUTES","aPos","GLMeshCache","GLTextureCache","GLImageCache","GradientRampAtlas","DrawBatch","createWhiteTexture","QUAD_VERTICES","QUAD_INDICES","getProgramSource","handle","previous","id","compiled","e","existing","kind","frag","uniforms","GLYPH_PAINT_VERT_SRC","GLYPH_PAINT_ATTRIBUTES","w","h","x","y","target","markAllFontsNotUploaded","prog","t0","counters","spanStats","resetBakeBudget","ctx","spans","next","si","span","commands","drawsBefore","spanT0","flushBatch","tolerance","dims","tex"],"sources":["../../src/renderer/WeaselRenderer.ts"],"sourcesContent":["import { createPaintResources, type OwnedPaintResources } from './paintResources';\nimport { ShaderProgram } from './shaders/ShaderProgram';\nimport {\n VERT_SRC,\n FRAG_SRC,\n VCOLOR_VERT_SRC,\n VCOLOR_FRAG_SRC,\n PATH_FILL_UNIFORMS,\n PATH_FILL_ATTRIBUTES,\n PATH_FILL_VCOLOR_ATTRIBUTES,\n} from './shaders/pathFill';\nimport {\n markAllFontsNotUploaded,\n resetBakeBudget,\n DEFAULT_BAKE_BUDGET,\n} from '@weasel-js/font';\nimport {\n IMAGE_VERT_SRC,\n IMAGE_FRAG_SRC,\n IMAGE_FILL_UNIFORMS,\n IMAGE_FILL_ATTRIBUTES,\n} from './shaders/imageFill';\nimport {\n BATCH_VERT_SRC,\n BATCH_FRAG_SRC,\n BATCH_FILL_UNIFORMS,\n BATCH_FILL_ATTRIBUTES,\n} from './shaders/batchFill';\nimport {\n GRAD_VERT_SRC,\n GRAD_FRAG_SRC,\n GRAD_FILL_UNIFORMS,\n GRAD_FILL_ATTRIBUTES,\n} from './shaders/gradFill';\nimport {\n PATTERN_VERT_SRC,\n PATTERN_FRAG_SRC,\n PATTERN_FILL_UNIFORMS,\n PATTERN_FILL_ATTRIBUTES,\n} from './shaders/patternFill';\nimport {\n GLYPH_PAINT_VERT_SRC,\n GLYPH_PATTERN_FRAG_SRC,\n GLYPH_GRAD_FRAG_SRC,\n GLYPH_TEXTURE_FRAG_SRC,\n GLYPH_PATTERN_UNIFORMS,\n GLYPH_GRAD_UNIFORMS,\n GLYPH_TEXTURE_UNIFORMS,\n GLYPH_PAINT_ATTRIBUTES,\n} from './shaders/glyphPaint';\nimport { PaintScratch } from './paintScratch';\nimport { GLMeshCache } from './cache/GLMeshCache';\nimport { GLTextureCache } from './cache/GLTextureCache';\nimport { GLImageCache, type ImageMinification } from './cache/GLImageCache';\nimport { GradientRampAtlas } from './cache/GradientRampAtlas';\nimport { GroupState } from './state/GroupState';\nimport type { DrawCommand } from './DrawCommand';\nimport type { GlMat3 } from './math/mat3';\nimport {\n dispatch, flushBatch, OUTLINE_MIN_SCREEN_PX, type DrawContext, type GlyphPaintKind,\n} from './draw';\nimport { DrawBatch } from './drawBatch';\nimport {\n CUSTOM_VERT_SRC, CUSTOM_ATTRIBUTES, CUSTOM_KIT_UNIFORMS,\n QUAD_VERTICES, QUAD_INDICES,\n} from './shaders/customPrelude';\nimport { getProgramSource, type ProgramSource, type ShaderProgramHandle } from './shaders/registerProgram';\nimport { EffectTargets } from './effects/EffectTargets';\nimport { extractUniformArrays, extractUniformNames } from './shaders/extractUniformNames';\n\n/** Every uniform a custom program may be written through. Both stages, not\n * just the fragment one: a name never looked up is written through `null`,\n * which GL accepts silently, so the uniform keeps its zero default. */\nfunction customUniformNames(vertSrc: string, fragSrc: string): string[] {\n return [...CUSTOM_KIT_UNIFORMS, ...extractUniformNames(vertSrc), ...extractUniformNames(fragSrc)];\n}\n\n/** Compile a registered program's source against `gl`, with every uniform\n * location and array base it may be written through. */\nfunction compileCustomProgram(gl: WebGL2RenderingContext, src: ProgramSource): ShaderProgram {\n const vertSrc = src.vert === '' ? CUSTOM_VERT_SRC : src.vert;\n const program = new ShaderProgram(gl, vertSrc, src.frag);\n program.lookupUniforms(customUniformNames(vertSrc, src.frag));\n program.lookupUniformArrays(new Map([...extractUniformArrays(vertSrc), ...extractUniformArrays(src.frag)]));\n program.lookupAttributes(CUSTOM_ATTRIBUTES);\n return program;\n}\n\n/** How to construct a `WeaselRenderer`: the GL context or canvas to draw\n * into, the output size, and the quality knobs that separate screen\n * rendering from print. */\n/** A run of top-level commands, `[start, end)` by index, that\n * {@link WeaselRenderer.lastFrameStats} accounts for separately — one render\n * layer's commands, say. Spans are in order and do not overlap. */\nexport interface RenderSpan {\n id: string;\n start: number;\n end: number;\n}\n\n/** Per-frame options for {@link WeaselRenderer.render}. */\nexport interface RenderOptions {\n spans?: readonly RenderSpan[];\n}\n\n/** What one span cost. A batched run is drawn when something closes it, so\n * its draw call counts toward the span that closed it. */\nexport interface RenderSpanStats {\n id: string;\n drawCalls: number;\n ms: number;\n}\n\n/** What one `render()` cost. See {@link WeaselRenderer.lastFrameStats}. */\nexport interface RenderStats {\n drawCalls: number;\n ms: number;\n spans: RenderSpanStats[];\n}\n\nfunction now(): number {\n return typeof performance !== 'undefined' ? performance.now() : Date.now();\n}\n\n/** Construction options for {@link WeaselRenderer}. Pass `gl` or `canvas`;\n * a context passed in must have a stencil buffer. */\nexport interface WeaselRendererOptions {\n /** A WebGL2 context to draw with. Wins over `canvas`. */\n gl?: WebGL2RenderingContext;\n /** A canvas to open a stencil-backed WebGL2 context on, when `gl` is absent. */\n canvas?: HTMLCanvasElement;\n /** Drawing size in CSS pixels. */\n width: number;\n /** Drawing size in CSS pixels. */\n height: number;\n /** Device pixels per CSS pixel. */\n dpr: number;\n /** MIN_FILTER strategy for image/pattern textures (`GLImageCache`).\n * Default `'linear'` — the existing screen behavior. The headless\n * `renderSceneToPixels` path passes `'mipmap'` for print-quality\n * minification. Explicitly passing `'linear'` is always valid. */\n imageMinification?: ImageMinification;\n /** Flatness tolerance for curve tessellation, in WORLD units (see\n * `TessellateOptions.flattenTolerance`). When set, path fills are\n * tessellated fresh at this tolerance per frame (transient pool) instead\n * of served from the Path-identity mesh cache — the cache key does not\n * include tolerance. Default: unset — the existing cached behavior at\n * `DEFAULT_FLATTEN_TOLERANCE`. The headless path derives this from the\n * requested output scale; screen callers normally leave it unset. */\n flattenTolerance?: number;\n /** Per-render synchronous bake budget for dynamic canvas-SDF glyphs.\n * Default DEFAULT_BAKE_BUDGET (16). The headless renderSceneToPixels\n * path passes Infinity so print never defers a glyph. */\n bakeBudget?: number;\n /** On-screen glyph size, in CSS pixels, at or above which text renders from\n * tessellated font outlines instead of a distance field — see\n * `OUTLINE_MIN_SCREEN_PX`. Only faces registered with\n * `registerFontOutlines` are affected; everything else keeps its SDF tier\n * whatever this says. Pass `Infinity` to disable the tier outright, or 0\n * to use outlines wherever they exist (what the headless path does, since\n * print has no reason to sample a field it could evaluate exactly). */\n textOutlineMinScreenSize?: number;\n}\n\n/** Where a renderer draws inside a buffer it does not own.\n *\n * The rect's SIZE is the renderer's own `width`/`height` — `resize()` owns\n * that, and a second copy here could disagree with the one `DrawContext`\n * reports to screen-space layers. */\nexport interface RenderTarget {\n /** Top-left of this renderer's output within the drawing buffer, in CSS\n * pixels, with the origin at the buffer's top-left. GL's bottom-left origin\n * is handled internally. */\n origin: { x: number; y: number };\n /** Clear colour and stencil within the rect before drawing. Default true.\n * Pass false only when the caller clears the whole buffer itself on behalf\n * of every co-tenant — a frame that clears neither inherits its neighbour's\n * stencil bits, and even-odd fills then fill their holes. */\n clear?: boolean;\n}\n\n/** Each glyph-paint program's fragment stage and the uniforms it looks up. */\nconst GLYPH_PAINT_SOURCES: Record<GlyphPaintKind, readonly [string, readonly string[]]> = {\n pattern: [GLYPH_PATTERN_FRAG_SRC, GLYPH_PATTERN_UNIFORMS],\n gradient: [GLYPH_GRAD_FRAG_SRC, GLYPH_GRAD_UNIFORMS],\n texture: [GLYPH_TEXTURE_FRAG_SRC, GLYPH_TEXTURE_UNIFORMS],\n};\n\n/**\n * The WebGL2 renderer: takes a list of draw commands and paints them.\n *\n * It knows nothing about the scene — commands are the whole interface, which\n * is what lets layers, HUD widgets and overlays all draw through the same\n * pipeline. GPU resources (meshes, textures, gradient ramps) are cached across\n * frames and keyed by identity, so re-issuing the same command is cheap.\n */\nexport class WeaselRenderer {\n private readonly gl: WebGL2RenderingContext;\n private stats: RenderStats = { drawCalls: 0, ms: 0, spans: [] };\n private pathFill: ShaderProgram;\n private pathFillVColor: ShaderProgram;\n private imageFill: ShaderProgram;\n private batchFill: ShaderProgram;\n private gradFill: ShaderProgram;\n private patternFill: ShaderProgram;\n /** Compiled on first use: only atlas text under a texture paint needs one. */\n private glyphPaintPrograms = new Map<GlyphPaintKind, ShaderProgram>();\n private meshCache: GLMeshCache;\n private textureCache: GLTextureCache;\n private imageCache: GLImageCache;\n private gradRamps: GradientRampAtlas;\n private programRegistry = new Map<string, ShaderProgram>();\n /** The source each compiled custom program was built from — or, after a\n * failed recompile, the source that failed, so it is not retried every frame. */\n private programSources = new Map<string, ProgramSource>();\n private quadVbo: WebGLBuffer | null = null;\n private quadIbo: WebGLBuffer | null = null;\n private drawBatch: DrawBatch;\n /** 1x1 white, so a batch flush sampling no image still samples something —\n * see `shaders/batchFill.ts`. */\n private whiteTexture: WebGLTexture | null = null;\n private readonly groupState = new GroupState();\n private widthCss: number;\n private heightCss: number;\n private dpr: number;\n private canvas: HTMLCanvasElement | null = null;\n private target: RenderTarget | null = null;\n /** Offscreen buffers for group effects. Allocates nothing until a group\n * with effects asks, so a canvas without them pays no memory. */\n private readonly effectTargets: EffectTargets;\n private readonly paintScratch: PaintScratch;\n private readonly imageMinification: ImageMinification;\n private flattenTolerance?: number;\n private readonly bakeBudget: number;\n private readonly textOutlineMinScreenSize: number;\n private contextLost = false;\n private boundOnLost = (e: Event) => this.onContextLost(e);\n private boundOnRestored = () => this.onContextRestored();\n /** True after `dispose()`. A disposed renderer ignores further `render()`\n * calls and `registerProgram()` calls. */\n private disposed = false;\n /** What registered paint kinds hang their GPU state on. One per context\n * generation: a restore releases it and opens the next. */\n private paintResources: OwnedPaintResources = createPaintResources();\n\n constructor(opts: WeaselRendererOptions) {\n if (!opts.gl && !opts.canvas) {\n throw new Error('WeaselRenderer requires either gl or canvas');\n }\n const gl = opts.gl ?? opts.canvas!.getContext('webgl2', { stencil: true });\n if (!gl) throw new Error('WeaselRenderer: WebGL2 not available');\n this.gl = gl as WebGL2RenderingContext;\n this.effectTargets = new EffectTargets(this.gl);\n this.paintScratch = new PaintScratch(this.gl);\n\n // Bits 0-7 are load-bearing: bit 0 for even-odd fills and stenciled\n // strokes, bits 1-7 for clip depth (`renderer/draw.ts`). Without a stencil\n // buffer both render wrong rather than not at all, so refuse the context\n // instead of painting a plausible lie. A stub that cannot answer is not a\n // stencil-less context — only an explicit `false` is.\n const attrs = typeof this.gl.getContextAttributes === 'function'\n ? this.gl.getContextAttributes()\n : null;\n if (attrs && attrs.stencil === false) {\n throw new Error(\n 'WeaselRenderer: the supplied WebGL2 context has no stencil buffer. '\n + 'Create it with { stencil: true } — clips and even-odd fills need one.',\n );\n }\n\n this.widthCss = opts.width;\n this.heightCss = opts.height;\n this.dpr = opts.dpr;\n this.imageMinification = opts.imageMinification ?? 'linear';\n this.flattenTolerance = opts.flattenTolerance;\n this.bakeBudget = opts.bakeBudget ?? DEFAULT_BAKE_BUDGET;\n this.textOutlineMinScreenSize = opts.textOutlineMinScreenSize ?? OUTLINE_MIN_SCREEN_PX;\n\n this.canvas = opts.canvas ?? null;\n if (this.canvas) {\n this.canvas.width = Math.round(opts.width * opts.dpr);\n this.canvas.height = Math.round(opts.height * opts.dpr);\n if (this.canvas.style) {\n this.canvas.style.width = `${opts.width}px`;\n this.canvas.style.height = `${opts.height}px`;\n }\n this.canvas.addEventListener('webglcontextlost', this.boundOnLost);\n this.canvas.addEventListener('webglcontextrestored', this.boundOnRestored);\n }\n\n this.applyGlState();\n this.applyTarget();\n\n this.pathFill = new ShaderProgram(this.gl, VERT_SRC, FRAG_SRC);\n this.pathFill.lookupUniforms(PATH_FILL_UNIFORMS);\n this.pathFill.lookupAttributes(PATH_FILL_ATTRIBUTES);\n\n this.pathFillVColor = new ShaderProgram(this.gl, VCOLOR_VERT_SRC, VCOLOR_FRAG_SRC);\n this.pathFillVColor.lookupUniforms(PATH_FILL_UNIFORMS);\n this.pathFillVColor.lookupAttributes(PATH_FILL_VCOLOR_ATTRIBUTES);\n\n this.imageFill = new ShaderProgram(this.gl, IMAGE_VERT_SRC, IMAGE_FRAG_SRC);\n this.imageFill.lookupUniforms(IMAGE_FILL_UNIFORMS);\n this.imageFill.lookupAttributes(IMAGE_FILL_ATTRIBUTES);\n\n this.batchFill = new ShaderProgram(this.gl, BATCH_VERT_SRC, BATCH_FRAG_SRC);\n this.batchFill.lookupUniforms(BATCH_FILL_UNIFORMS);\n this.batchFill.lookupAttributes(BATCH_FILL_ATTRIBUTES);\n\n this.gradFill = new ShaderProgram(this.gl, GRAD_VERT_SRC, GRAD_FRAG_SRC);\n this.gradFill.lookupUniforms(GRAD_FILL_UNIFORMS);\n this.gradFill.lookupAttributes(GRAD_FILL_ATTRIBUTES);\n\n this.patternFill = new ShaderProgram(this.gl, PATTERN_VERT_SRC, PATTERN_FRAG_SRC);\n this.patternFill.lookupUniforms(PATTERN_FILL_UNIFORMS);\n this.patternFill.lookupAttributes(PATTERN_FILL_ATTRIBUTES);\n\n const aPos = this.pathFill.attribute('a_position');\n if (aPos === undefined) throw new Error('WeaselRenderer: a_position not found in path-fill shader');\n this.meshCache = new GLMeshCache(this.gl, aPos);\n this.textureCache = new GLTextureCache(this.gl);\n this.imageCache = new GLImageCache(this.gl, this.imageMinification);\n this.gradRamps = new GradientRampAtlas(this.gl);\n this.uploadQuadGeometry();\n this.drawBatch = new DrawBatch(this.gl, this.batchFill);\n this.whiteTexture = createWhiteTexture(this.gl);\n }\n\n private uploadQuadGeometry(): void {\n const gl = this.gl;\n this.quadVbo = gl.createBuffer();\n this.quadIbo = gl.createBuffer();\n if (!this.quadVbo || !this.quadIbo) {\n throw new Error('WeaselRenderer: failed to create quad geometry buffers');\n }\n gl.bindBuffer(gl.ARRAY_BUFFER, this.quadVbo);\n gl.bufferData(gl.ARRAY_BUFFER, QUAD_VERTICES, gl.STATIC_DRAW);\n gl.bindBuffer(gl.ARRAY_BUFFER, null);\n\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.quadIbo);\n gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, QUAD_INDICES, gl.STATIC_DRAW);\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);\n }\n\n /**\n * Compile a consumer-registered shader program against this renderer's GL context.\n *\n * Call once per renderer after the module-level `registerProgram()`.\n * Throws `ShaderCompileError` if compilation fails.\n *\n * In dev mode, calling again with the same handle replaces the compiled program.\n *\n * @experimental\n */\n registerProgram(handle: ShaderProgramHandle): void {\n // A disposed renderer must not compile programs into the cleared registry.\n if (this.disposed) return;\n const src = getProgramSource(handle.id);\n if (!src) {\n throw new Error(\n `WeaselRenderer.registerProgram: program \"${handle.id}\" not found in source registry. ` +\n `Call the module-level registerProgram() first.`,\n );\n }\n this.installProgram(handle.id, src, compileCustomProgram(this.gl, src));\n }\n\n private installProgram(id: string, src: ProgramSource, program: ShaderProgram): void {\n const previous = this.programRegistry.get(id);\n if (previous) this.gl.deleteProgram(previous.handle);\n this.programRegistry.set(id, program);\n this.programSources.set(id, src);\n }\n\n /** Recompile every program whose module-level source was re-registered\n * since it was compiled. A source that fails keeps the old program drawing. */\n private refreshPrograms(): void {\n for (const [id, compiled] of this.programSources) {\n const src = getProgramSource(id);\n if (!src || src === compiled) continue;\n if (src.vert === compiled.vert && src.frag === compiled.frag) {\n this.programSources.set(id, src);\n continue;\n }\n try {\n this.installProgram(id, src, compileCustomProgram(this.gl, src));\n } catch (e) {\n this.programSources.set(id, src);\n console.error(`weasel: failed to recompile re-registered program \"${id}\"; still drawing the previous one:`, e);\n }\n }\n }\n\n /**\n * The compiled program for `id`, compiling it against this context on first\n * use. Unlike {@link registerProgram} this neither throws on a missing\n * source nor on a compile failure: a paint kind asking for a program it\n * never registered must decline the paint, not take down the frame.\n */\n private ensureProgram(id: string): ShaderProgram | null {\n const existing = this.programRegistry.get(id);\n if (existing) return existing;\n if (this.disposed || !getProgramSource(id)) return null;\n try {\n this.registerProgram({ id });\n } catch (e) {\n console.error(`weasel: failed to compile paint program \"${id}\":`, e);\n return null;\n }\n return this.programRegistry.get(id) ?? null;\n }\n\n private glyphPaintProgram(kind: GlyphPaintKind): ShaderProgram {\n const existing = this.glyphPaintPrograms.get(kind);\n if (existing) return existing;\n const [frag, uniforms] = GLYPH_PAINT_SOURCES[kind];\n const program = new ShaderProgram(this.gl, GLYPH_PAINT_VERT_SRC, frag);\n program.lookupUniforms(uniforms);\n program.lookupAttributes(GLYPH_PAINT_ATTRIBUTES);\n this.glyphPaintPrograms.set(kind, program);\n return program;\n }\n\n /** The GL state every frame assumes. Applied per `render()` rather than once\n * at construction because a co-tenant sharing this context moves all of it\n * between our frames. */\n private applyGlState(): void {\n const gl = this.gl;\n gl.enable(gl.BLEND);\n gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);\n gl.disable(gl.DEPTH_TEST);\n gl.disable(gl.CULL_FACE);\n gl.clearColor(0, 0, 0, 0);\n }\n\n /** Viewport and scissor for this frame. Re-applied inside `render()` because\n * a co-tenant on the same context moves both between our frames. */\n private applyTarget(): void {\n const gl = this.gl;\n const w = Math.round(this.widthCss * this.dpr);\n const h = Math.round(this.heightCss * this.dpr);\n if (!this.target) {\n gl.disable(gl.SCISSOR_TEST);\n gl.viewport(0, 0, w, h);\n return;\n }\n const x = Math.round(this.target.origin.x * this.dpr);\n const y = gl.drawingBufferHeight - Math.round(this.target.origin.y * this.dpr) - h;\n gl.viewport(x, y, w, h);\n gl.scissor(x, y, w, h);\n gl.enable(gl.SCISSOR_TEST);\n }\n\n /** Confine this renderer to a rect of its drawing buffer, or pass null to\n * give it the whole buffer back. Takes effect from the next `render()`. */\n setTarget(target: RenderTarget | null): void {\n this.target = target;\n this.applyTarget();\n }\n\n getTarget(): RenderTarget | null {\n return this.target;\n }\n\n isContextLost(): boolean {\n return this.contextLost;\n }\n\n private onContextLost(e: Event): void {\n e.preventDefault();\n this.contextLost = true;\n }\n\n private onContextRestored(): void {\n this.contextLost = false;\n this.applyGlState();\n this.applyTarget();\n // Every framebuffer, texture and renderbuffer the pool held died with the\n // context. `releaseAll` deletes handles that are already gone, which is a\n // no-op, and drops the bookkeeping that would otherwise hand a dead FBO to\n // the next group with effects.\n this.effectTargets.releaseAll();\n this.paintScratch.release();\n this.paintResources.release();\n this.paintResources = createPaintResources();\n this.pathFill = new ShaderProgram(this.gl, VERT_SRC, FRAG_SRC);\n this.pathFill.lookupUniforms(PATH_FILL_UNIFORMS);\n this.pathFill.lookupAttributes(PATH_FILL_ATTRIBUTES);\n this.pathFillVColor = new ShaderProgram(this.gl, VCOLOR_VERT_SRC, VCOLOR_FRAG_SRC);\n this.pathFillVColor.lookupUniforms(PATH_FILL_UNIFORMS);\n this.pathFillVColor.lookupAttributes(PATH_FILL_VCOLOR_ATTRIBUTES); this.imageFill = new ShaderProgram(this.gl, IMAGE_VERT_SRC, IMAGE_FRAG_SRC);\n this.imageFill.lookupUniforms(IMAGE_FILL_UNIFORMS);\n this.imageFill.lookupAttributes(IMAGE_FILL_ATTRIBUTES);\n\n this.batchFill = new ShaderProgram(this.gl, BATCH_VERT_SRC, BATCH_FRAG_SRC);\n this.batchFill.lookupUniforms(BATCH_FILL_UNIFORMS);\n this.batchFill.lookupAttributes(BATCH_FILL_ATTRIBUTES);\n this.gradFill = new ShaderProgram(this.gl, GRAD_VERT_SRC, GRAD_FRAG_SRC);\n this.gradFill.lookupUniforms(GRAD_FILL_UNIFORMS);\n this.gradFill.lookupAttributes(GRAD_FILL_ATTRIBUTES);\n\n this.patternFill = new ShaderProgram(this.gl, PATTERN_VERT_SRC, PATTERN_FRAG_SRC);\n this.patternFill.lookupUniforms(PATTERN_FILL_UNIFORMS);\n this.patternFill.lookupAttributes(PATTERN_FILL_ATTRIBUTES);\n this.glyphPaintPrograms.clear();\n const aPos = this.pathFill.attribute('a_position');\n if (aPos === undefined) throw new Error('a_position missing after restore');\n this.meshCache = new GLMeshCache(this.gl, aPos);\n this.textureCache = new GLTextureCache(this.gl);\n this.imageCache.abandon();\n this.imageCache = new GLImageCache(this.gl, this.imageMinification);\n this.gradRamps = new GradientRampAtlas(this.gl);\n markAllFontsNotUploaded();\n\n this.uploadQuadGeometry();\n this.drawBatch = new DrawBatch(this.gl, this.batchFill);\n this.whiteTexture = createWhiteTexture(this.gl);\n for (const id of this.programRegistry.keys()) {\n const src = getProgramSource(id);\n if (!src) continue;\n try {\n this.programRegistry.set(id, compileCustomProgram(this.gl, src));\n this.programSources.set(id, src);\n } catch (e) {\n console.error(`weasel: failed to recompile program \"${id}\" after context restore:`, e);\n }\n }\n }\n\n /** Free every GL resource this renderer created and detach context-loss\n * listeners when a canvas was supplied. Idempotent.\n *\n * That covers the built-in and consumer-registered programs (their shaders\n * go with them), the shared quad geometry, the batch, the effect targets,\n * and every cache: atlas and gradient-ramp textures, bitmap and pattern\n * textures, and meshes — transient, persistent, and those whose Path was\n * already collected. The context itself is left alone, so a caller-owned\n * context can host renderer after renderer without accumulating anything.\n *\n * A disposed renderer ignores further `render()` and `registerProgram()`\n * calls. */\n dispose(): void {\n if (this.disposed) return;\n this.disposed = true;\n const gl = this.gl;\n if (this.canvas) {\n this.canvas.removeEventListener('webglcontextlost', this.boundOnLost);\n this.canvas.removeEventListener('webglcontextrestored', this.boundOnRestored);\n }\n this.meshCache.dispose();\n this.paintResources.release();\n for (const prog of [\n this.pathFill, this.pathFillVColor, this.imageFill, this.batchFill, this.gradFill, this.patternFill,\n ...this.glyphPaintPrograms.values(),\n ]) {\n gl.deleteProgram(prog.handle);\n }\n this.glyphPaintPrograms.clear();\n for (const prog of this.programRegistry.values()) {\n gl.deleteProgram(prog.handle);\n }\n this.programRegistry.clear();\n this.programSources.clear();\n this.textureCache.free();\n this.imageCache.dispose();\n this.gradRamps.free();\n if (this.quadVbo) gl.deleteBuffer(this.quadVbo);\n if (this.quadIbo) gl.deleteBuffer(this.quadIbo);\n this.drawBatch.dispose();\n if (this.whiteTexture) gl.deleteTexture(this.whiteTexture);\n this.whiteTexture = null;\n this.effectTargets.releaseAll();\n this.paintScratch.release();\n }\n\n /**\n * Draw one frame.\n *\n * `viewMatrix` is the frame's world→screen transform. It is only consulted\n * by `units: 'world'` gradients, which fall back to screen space without\n * it — every other command carries its own transform in the stream, so\n * callers with no view concept can keep calling `render(commands)`.\n */\n render(commands: DrawCommand[], viewMatrix?: GlMat3, opts?: RenderOptions): void {\n if (this.contextLost || this.disposed) return;\n const t0 = now();\n const counters = { drawCalls: 0 };\n const spanStats: RenderSpanStats[] = [];\n this.stats = { drawCalls: 0, ms: 0, spans: spanStats };\n const gl = this.gl;\n // Free GL resources whose Mesh was GC'd since the last frame. Done here\n // (top of render, before any draws) because GL state is known clean —\n // no VAO bound, no draw in flight. Deleting from the FinalizationRegistry\n // callback directly was racy and caused mid-draw crashes.\n this.meshCache.drainPendingDeletes();\n this.refreshPrograms();\n // New frame: refill the dynamic-glyph synchronous bake budget.\n resetBakeBudget(this.bakeBudget);\n this.groupState.reset();\n this.applyGlState();\n this.applyTarget();\n // Ensure all stencil bits are cleared regardless of any mask left over\n // from the previous frame's clip ops.\n if (this.target?.clear !== false) {\n gl.stencilMask(0xFF);\n gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);\n }\n const ctx: DrawContext = {\n gl,\n pathFill: this.pathFill,\n pathFillVColor: this.pathFillVColor,\n imageFill: this.imageFill,\n batchFill: this.batchFill,\n gradFill: this.gradFill,\n patternFill: this.patternFill,\n meshCache: this.meshCache,\n textureCache: this.textureCache,\n imageCache: this.imageCache,\n gradRamps: this.gradRamps,\n programRegistry: this.programRegistry,\n ensureProgram: (id) => this.ensureProgram(id),\n glyphPaintProgram: (kind) => this.glyphPaintProgram(kind),\n quadVbo: this.quadVbo,\n quadIbo: this.quadIbo,\n drawBatch: this.drawBatch,\n whiteTexture: this.whiteTexture,\n state: this.groupState,\n widthCss: this.widthCss,\n heightCss: this.heightCss,\n clipDepth: 0,\n clipStack: [],\n flattenTolerance: this.flattenTolerance,\n textOutlineMinScreenSize: this.textOutlineMinScreenSize,\n viewMatrix,\n effectTargets: this.effectTargets,\n paintScratch: this.paintScratch,\n renderTarget: null,\n deviceWidth: Math.round(this.widthCss * this.dpr),\n deviceHeight: Math.round(this.heightCss * this.dpr),\n restoreTargetRect: () => this.applyTarget(),\n paintResources: this.paintResources,\n stats: counters,\n };\n // A frame that threw part-way left its clip here.\n this.drawBatch.clip = null;\n const spans = opts?.spans ?? [];\n let next = 0;\n for (let si = 0; si < spans.length; si++) {\n const span = spans[si];\n for (; next < span.start; next++) dispatch(ctx, commands[next]);\n const drawsBefore = counters.drawCalls;\n const spanT0 = now();\n for (; next < span.end; next++) dispatch(ctx, commands[next]);\n // The stream's closing flush belongs to whatever it closes.\n if (si === spans.length - 1 && next >= commands.length) flushBatch(ctx);\n spanStats.push({ id: span.id, drawCalls: counters.drawCalls - drawsBefore, ms: now() - spanT0 });\n }\n for (; next < commands.length; next++) dispatch(ctx, commands[next]);\n // The stream ended, so whatever is still staged has nothing left that\n // could merge with it.\n flushBatch(ctx);\n // Free transient resources allocated during this frame (e.g. per-frame\n // stroke ribbons from tessellateStroke). Done after all draws complete\n // so we never delete a buffer that's still bound to a pending draw.\n this.meshCache.freeTransient();\n this.stats.drawCalls = counters.drawCalls;\n this.stats.ms = now() - t0;\n }\n\n /** What the last `render()` cost: GL draw calls issued and CPU milliseconds\n * spent issuing them, in total and per requested span. GPU time is not\n * measured; the GPU runs asynchronously. */\n lastFrameStats(): RenderStats {\n return this.stats;\n }\n\n /** Change the curve-flattening tolerance after construction. `undefined`\n * restores the cached `DEFAULT_FLATTEN_TOLERANCE` route. */\n setFlattenTolerance(tolerance: number | undefined): void {\n this.flattenTolerance = tolerance;\n }\n\n resize(dims: { width: number; height: number; dpr: number }): void {\n this.widthCss = dims.width;\n this.heightCss = dims.height;\n this.dpr = dims.dpr;\n if (this.canvas) {\n this.canvas.width = Math.round(dims.width * dims.dpr);\n this.canvas.height = Math.round(dims.height * dims.dpr);\n if (this.canvas.style) {\n this.canvas.style.width = `${dims.width}px`;\n this.canvas.style.height = `${dims.height}px`;\n }\n }\n this.applyTarget();\n }\n\n /** @internal */ _gl(): WebGL2RenderingContext { return this.gl; }\n /** @internal */ _pathFill(): ShaderProgram { return this.pathFill; }\n /** @internal */ _pathFillVColor(): ShaderProgram { return this.pathFillVColor; }\n /** @internal */ _drawBatch(): DrawBatch { return this.drawBatch; }\n /** @internal */ _imageFill(): ShaderProgram { return this.imageFill; }\n /** @internal */ _batchFill(): ShaderProgram { return this.batchFill; }\n /** @internal */ _gradFill(): ShaderProgram { return this.gradFill; }\n /** @internal */ _patternFill(): ShaderProgram { return this.patternFill; }\n /** @internal */ _meshCache(): GLMeshCache { return this.meshCache; }\n /** @internal */ _textureCache(): GLTextureCache { return this.textureCache; }\n /** @internal */ _imageCache(): GLImageCache { return this.imageCache; }\n /** @internal */ _gradRamps(): GradientRampAtlas { return this.gradRamps; }\n /** @internal */ _groupState(): GroupState { return this.groupState; }\n /** @internal */ _widthCss(): number { return this.widthCss; }\n /** @internal */ _heightCss(): number { return this.heightCss; }\n /** @internal */ _dpr(): number { return this.dpr; }\n}\n\n/** The 1x1 opaque white texture a batch flush binds when its run samples no\n * image. NEAREST both ways: one texel, so there is nothing to filter, and a\n * mipmap filter on a texture with no mipmaps is incomplete. */\nfunction createWhiteTexture(gl: WebGL2RenderingContext): WebGLTexture | null {\n const tex = gl.createTexture();\n if (!tex) return null;\n gl.bindTexture(gl.TEXTURE_2D, tex);\n gl.texImage2D(\n gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,\n new Uint8Array([255, 255, 255, 255]),\n );\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n gl.bindTexture(gl.TEXTURE_2D, null);\n return tex;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"WeaselRenderer.js","names":["customUniformNames","CUSTOM_KIT_UNIFORMS","extractUniformNames","vertSrc","fragSrc","compileCustomProgram","src","CUSTOM_VERT_SRC","program","ShaderProgram","gl","extractUniformArrays","CUSTOM_ATTRIBUTES","now","GLYPH_PAINT_SOURCES","GLYPH_PATTERN_FRAG_SRC","GLYPH_PATTERN_UNIFORMS","GLYPH_GRAD_FRAG_SRC","GLYPH_GRAD_UNIFORMS","GLYPH_TEXTURE_FRAG_SRC","GLYPH_TEXTURE_UNIFORMS","WeaselRenderer","GroupState","createPaintResources","opts","EffectTargets","PaintScratch","attrs","DEFAULT_BAKE_BUDGET","OUTLINE_MIN_SCREEN_PX","lossTarget","VERT_SRC","FRAG_SRC","PATH_FILL_UNIFORMS","PATH_FILL_ATTRIBUTES","VCOLOR_VERT_SRC","VCOLOR_FRAG_SRC","PATH_FILL_VCOLOR_ATTRIBUTES","BATCH_VERT_SRC","BATCH_FRAG_SRC","BATCH_FILL_UNIFORMS","BATCH_FILL_ATTRIBUTES","GRAD_VERT_SRC","GRAD_FRAG_SRC","GRAD_FILL_UNIFORMS","GRAD_FILL_ATTRIBUTES","PATTERN_VERT_SRC","PATTERN_FRAG_SRC","PATTERN_FILL_UNIFORMS","PATTERN_FILL_ATTRIBUTES","GLMeshCache","GLTextureCache","GLImageCache","GradientRampAtlas","DrawBatch","createWhiteTexture","QUAD_VERTICES","QUAD_INDICES","getProgramSource","handle","previous","id","compiled","e","existing","kind","frag","uniforms","GLYPH_PAINT_VERT_SRC","GLYPH_PAINT_ATTRIBUTES","ox","oy","x","y","width","height","glY","target","markAllFontsNotUploaded","prog","t0","counters","spanStats","textureRegistry","resetBakeBudget","device","ctx","spans","next","si","span","commands","drawsBefore","spanT0","flushBatch","tolerance","dims","tex"],"sources":["../../src/renderer/WeaselRenderer.ts"],"sourcesContent":["import { createPaintResources, type OwnedPaintResources } from './paintResources';\nimport { ShaderProgram } from './shaders/ShaderProgram';\nimport {\n VERT_SRC,\n FRAG_SRC,\n VCOLOR_VERT_SRC,\n VCOLOR_FRAG_SRC,\n PATH_FILL_UNIFORMS,\n PATH_FILL_ATTRIBUTES,\n PATH_FILL_VCOLOR_ATTRIBUTES,\n} from './shaders/pathFill';\nimport {\n markAllFontsNotUploaded,\n resetBakeBudget,\n DEFAULT_BAKE_BUDGET,\n} from '@weasel-js/font';\nimport {\n BATCH_VERT_SRC,\n BATCH_FRAG_SRC,\n BATCH_FILL_UNIFORMS,\n BATCH_FILL_ATTRIBUTES,\n} from './shaders/batchFill';\nimport {\n GRAD_VERT_SRC,\n GRAD_FRAG_SRC,\n GRAD_FILL_UNIFORMS,\n GRAD_FILL_ATTRIBUTES,\n} from './shaders/gradFill';\nimport {\n PATTERN_VERT_SRC,\n PATTERN_FRAG_SRC,\n PATTERN_FILL_UNIFORMS,\n PATTERN_FILL_ATTRIBUTES,\n} from './shaders/patternFill';\nimport {\n GLYPH_PAINT_VERT_SRC,\n GLYPH_PATTERN_FRAG_SRC,\n GLYPH_GRAD_FRAG_SRC,\n GLYPH_TEXTURE_FRAG_SRC,\n GLYPH_PATTERN_UNIFORMS,\n GLYPH_GRAD_UNIFORMS,\n GLYPH_TEXTURE_UNIFORMS,\n GLYPH_PAINT_ATTRIBUTES,\n} from './shaders/glyphPaint';\nimport { PaintScratch } from './paintScratch';\nimport { GLMeshCache } from './cache/GLMeshCache';\nimport { GLTextureCache } from './cache/GLTextureCache';\nimport { textureRegistry } from './textures/registerTexture';\nimport { GLImageCache, type ImageMinification } from './cache/GLImageCache';\nimport { GradientRampAtlas } from './cache/GradientRampAtlas';\nimport { GroupState } from './state/GroupState';\nimport type { DrawCommand } from './DrawCommand';\nimport type { GlMat3 } from './math/mat3';\nimport {\n dispatch, flushBatch, OUTLINE_MIN_SCREEN_PX, type DrawContext, type GlyphPaintKind,\n} from './draw';\nimport { DrawBatch } from './drawBatch';\nimport {\n CUSTOM_VERT_SRC, CUSTOM_ATTRIBUTES, CUSTOM_KIT_UNIFORMS,\n QUAD_VERTICES, QUAD_INDICES,\n} from './shaders/customPrelude';\nimport { getProgramSource, type ProgramSource, type ShaderProgramHandle } from './shaders/registerProgram';\nimport { EffectTargets } from './effects/EffectTargets';\nimport { extractUniformArrays, extractUniformNames } from './shaders/extractUniformNames';\n\n/** Every uniform a custom program may be written through. Both stages, not\n * just the fragment one: a name never looked up is written through `null`,\n * which GL accepts silently, so the uniform keeps its zero default. */\nfunction customUniformNames(vertSrc: string, fragSrc: string): string[] {\n return [...CUSTOM_KIT_UNIFORMS, ...extractUniformNames(vertSrc), ...extractUniformNames(fragSrc)];\n}\n\n/** Compile a registered program's source against `gl`, with every uniform\n * location and array base it may be written through. */\nfunction compileCustomProgram(gl: WebGL2RenderingContext, src: ProgramSource): ShaderProgram {\n const vertSrc = src.vert === '' ? CUSTOM_VERT_SRC : src.vert;\n const program = new ShaderProgram(gl, vertSrc, src.frag);\n program.lookupUniforms(customUniformNames(vertSrc, src.frag));\n program.lookupUniformArrays(new Map([...extractUniformArrays(vertSrc), ...extractUniformArrays(src.frag)]));\n program.lookupAttributes(CUSTOM_ATTRIBUTES);\n return program;\n}\n\n/** A run of top-level commands, `[start, end)` by index, that\n * {@link WeaselRenderer.lastFrameStats} accounts for separately — one render\n * layer's commands, say. Spans are in order and do not overlap. */\nexport interface RenderSpan {\n id: string;\n start: number;\n end: number;\n}\n\n/** Per-frame options for {@link WeaselRenderer.render}. */\nexport interface RenderOptions {\n spans?: readonly RenderSpan[];\n}\n\n/** What one span cost. A batched run is drawn when something closes it, so\n * its draw call counts toward the span that closed it. */\nexport interface RenderSpanStats {\n id: string;\n drawCalls: number;\n ms: number;\n}\n\n/** What one `render()` cost. See {@link WeaselRenderer.lastFrameStats}. */\nexport interface RenderStats {\n drawCalls: number;\n ms: number;\n spans: RenderSpanStats[];\n}\n\nfunction now(): number {\n return typeof performance !== 'undefined' ? performance.now() : Date.now();\n}\n\n/** Construction options for {@link WeaselRenderer}. Pass `gl` or `canvas`;\n * a context passed in must have a stencil buffer. */\nexport interface WeaselRendererOptions {\n /** A WebGL2 context to draw with. Wins over `canvas`. */\n gl?: WebGL2RenderingContext;\n /** A canvas to open a stencil-backed WebGL2 context on, when `gl` is absent. */\n canvas?: HTMLCanvasElement;\n /** Drawing size in CSS pixels. */\n width: number;\n /** Drawing size in CSS pixels. */\n height: number;\n /** Device pixels per CSS pixel. */\n dpr: number;\n /** MIN_FILTER strategy for image/pattern textures (`GLImageCache`).\n * Default `'linear'` — the existing screen behavior. The headless\n * `renderSceneToPixels` path passes `'mipmap'` for print-quality\n * minification. Explicitly passing `'linear'` is always valid. */\n imageMinification?: ImageMinification;\n /** Flatness tolerance for curve tessellation, in WORLD units (see\n * `TessellateOptions.flattenTolerance`). When set, path fills are\n * tessellated fresh at this tolerance per frame (transient pool) instead\n * of served from the Path-identity mesh cache — the cache key does not\n * include tolerance. Default: unset — the existing cached behavior at\n * `DEFAULT_FLATTEN_TOLERANCE`. The headless path derives this from the\n * requested output scale; screen callers normally leave it unset. */\n flattenTolerance?: number;\n /** Per-render synchronous bake budget for dynamic canvas-SDF glyphs.\n * Default DEFAULT_BAKE_BUDGET (16). The headless renderSceneToPixels\n * path passes Infinity so print never defers a glyph. */\n bakeBudget?: number;\n /** On-screen glyph size, in CSS pixels, at or above which text renders from\n * tessellated font outlines instead of a distance field — see\n * `OUTLINE_MIN_SCREEN_PX`. Only faces registered with\n * `registerFontOutlines` are affected; everything else keeps its SDF tier\n * whatever this says. Pass `Infinity` to disable the tier outright, or 0\n * to use outlines wherever they exist (what the headless path does, since\n * print has no reason to sample a field it could evaluate exactly). */\n textOutlineMinScreenSize?: number;\n}\n\n/** Where a renderer draws inside a buffer it does not own.\n *\n * The rect's SIZE is the renderer's own `width`/`height` — `resize()` owns\n * that, and a second copy here could disagree with the one `DrawContext`\n * reports to screen-space layers. */\nexport interface RenderTarget {\n /** Top-left of this renderer's output within the drawing buffer, in CSS\n * pixels, with the origin at the buffer's top-left. GL's bottom-left origin\n * is handled internally. */\n origin: { x: number; y: number };\n /** Clear colour and stencil within the rect before drawing. Default true.\n * Pass false only when the caller clears the whole buffer itself on behalf\n * of every co-tenant — a frame that clears neither inherits its neighbour's\n * stencil bits, and even-odd fills then fill their holes. */\n clear?: boolean;\n}\n\n/** Each glyph-paint program's fragment stage and the uniforms it looks up. */\nconst GLYPH_PAINT_SOURCES: Record<GlyphPaintKind, readonly [string, readonly string[]]> = {\n pattern: [GLYPH_PATTERN_FRAG_SRC, GLYPH_PATTERN_UNIFORMS],\n gradient: [GLYPH_GRAD_FRAG_SRC, GLYPH_GRAD_UNIFORMS],\n texture: [GLYPH_TEXTURE_FRAG_SRC, GLYPH_TEXTURE_UNIFORMS],\n};\n\n/**\n * The WebGL2 renderer: takes a list of draw commands and paints them.\n *\n * It knows nothing about the scene — commands are the whole interface, which\n * is what lets layers, HUD widgets and overlays all draw through the same\n * pipeline. GPU resources (meshes, textures, gradient ramps) are cached across\n * frames and keyed by identity, so re-issuing the same command is cheap.\n */\nexport class WeaselRenderer {\n private readonly gl: WebGL2RenderingContext;\n private stats: RenderStats = { drawCalls: 0, ms: 0, spans: [] };\n private pathFill: ShaderProgram;\n private pathFillVColor: ShaderProgram;\n private batchFill: ShaderProgram;\n private gradFill: ShaderProgram;\n private patternFill: ShaderProgram;\n /** Compiled on first use: only atlas text under a texture paint needs one. */\n private glyphPaintPrograms = new Map<GlyphPaintKind, ShaderProgram>();\n private meshCache: GLMeshCache;\n private textureCache: GLTextureCache;\n private imageCache: GLImageCache;\n private gradRamps: GradientRampAtlas;\n private programRegistry = new Map<string, ShaderProgram>();\n /** The source each compiled custom program was built from — or, after a\n * failed recompile, the source that failed, so it is not retried every frame. */\n private programSources = new Map<string, ProgramSource>();\n private quadVbo: WebGLBuffer | null = null;\n private quadIbo: WebGLBuffer | null = null;\n private drawBatch: DrawBatch;\n /** 1x1 white, so a batch flush sampling no image still samples something —\n * see `shaders/batchFill.ts`. */\n private whiteTexture: WebGLTexture | null = null;\n private readonly groupState = new GroupState();\n private widthCss: number;\n private heightCss: number;\n private dpr: number;\n private canvas: HTMLCanvasElement | null = null;\n /** Where context loss is announced: the context's own canvas, which the\n * renderer need not own the size of. */\n private lossTarget: EventTarget | null = null;\n private target: RenderTarget | null = null;\n /** Offscreen buffers for group effects. Allocates nothing until a group\n * with effects asks, so a canvas without them pays no memory. */\n private readonly effectTargets: EffectTargets;\n private readonly paintScratch: PaintScratch;\n private readonly imageMinification: ImageMinification;\n private flattenTolerance?: number;\n private readonly bakeBudget: number;\n private readonly textOutlineMinScreenSize: number;\n private contextLost = false;\n private boundOnLost = (e: Event) => this.onContextLost(e);\n private boundOnRestored = () => this.onContextRestored();\n /** True after `dispose()`. A disposed renderer ignores further `render()`\n * calls and `registerProgram()` calls. */\n private disposed = false;\n /** What registered paint kinds hang their GPU state on. One per context\n * generation: a restore releases it and opens the next. */\n private paintResources: OwnedPaintResources = createPaintResources();\n\n constructor(opts: WeaselRendererOptions) {\n if (!opts.gl && !opts.canvas) {\n throw new Error('WeaselRenderer requires either gl or canvas');\n }\n const gl = opts.gl ?? opts.canvas!.getContext('webgl2', { stencil: true });\n if (!gl) throw new Error('WeaselRenderer: WebGL2 not available');\n this.gl = gl as WebGL2RenderingContext;\n this.effectTargets = new EffectTargets(this.gl);\n this.paintScratch = new PaintScratch(this.gl);\n\n // Bits 0-7 are load-bearing: bit 0 for even-odd fills and stenciled\n // strokes, bits 1-7 for clip depth (`renderer/draw.ts`). Without a stencil\n // buffer both render wrong rather than not at all, so refuse the context\n // instead of painting a plausible lie. A stub that cannot answer is not a\n // stencil-less context — only an explicit `false` is.\n const attrs = typeof this.gl.getContextAttributes === 'function'\n ? this.gl.getContextAttributes()\n : null;\n if (attrs && attrs.stencil === false) {\n throw new Error(\n 'WeaselRenderer: the supplied WebGL2 context has no stencil buffer. '\n + 'Create it with { stencil: true } — clips and even-odd fills need one.',\n );\n }\n\n this.widthCss = opts.width;\n this.heightCss = opts.height;\n this.dpr = opts.dpr;\n this.imageMinification = opts.imageMinification ?? 'linear';\n this.flattenTolerance = opts.flattenTolerance;\n this.bakeBudget = opts.bakeBudget ?? DEFAULT_BAKE_BUDGET;\n this.textOutlineMinScreenSize = opts.textOutlineMinScreenSize ?? OUTLINE_MIN_SCREEN_PX;\n\n this.canvas = opts.canvas ?? null;\n if (this.canvas) {\n this.canvas.width = Math.round(opts.width * opts.dpr);\n this.canvas.height = Math.round(opts.height * opts.dpr);\n if (this.canvas.style) {\n this.canvas.style.width = `${opts.width}px`;\n this.canvas.style.height = `${opts.height}px`;\n }\n }\n const lossTarget: Partial<EventTarget> | null = this.canvas ?? this.gl.canvas ?? null;\n if (lossTarget && typeof lossTarget.addEventListener === 'function') {\n this.lossTarget = lossTarget as EventTarget;\n this.lossTarget.addEventListener('webglcontextlost', this.boundOnLost);\n this.lossTarget.addEventListener('webglcontextrestored', this.boundOnRestored);\n }\n\n this.applyGlState();\n this.applyTarget();\n\n this.pathFill = new ShaderProgram(this.gl, VERT_SRC, FRAG_SRC);\n this.pathFill.lookupUniforms(PATH_FILL_UNIFORMS);\n this.pathFill.lookupAttributes(PATH_FILL_ATTRIBUTES);\n\n this.pathFillVColor = new ShaderProgram(this.gl, VCOLOR_VERT_SRC, VCOLOR_FRAG_SRC);\n this.pathFillVColor.lookupUniforms(PATH_FILL_UNIFORMS);\n this.pathFillVColor.lookupAttributes(PATH_FILL_VCOLOR_ATTRIBUTES);\n\n this.batchFill = new ShaderProgram(this.gl, BATCH_VERT_SRC, BATCH_FRAG_SRC);\n this.batchFill.lookupUniforms(BATCH_FILL_UNIFORMS);\n this.batchFill.lookupAttributes(BATCH_FILL_ATTRIBUTES);\n\n this.gradFill = new ShaderProgram(this.gl, GRAD_VERT_SRC, GRAD_FRAG_SRC);\n this.gradFill.lookupUniforms(GRAD_FILL_UNIFORMS);\n this.gradFill.lookupAttributes(GRAD_FILL_ATTRIBUTES);\n\n this.patternFill = new ShaderProgram(this.gl, PATTERN_VERT_SRC, PATTERN_FRAG_SRC);\n this.patternFill.lookupUniforms(PATTERN_FILL_UNIFORMS);\n this.patternFill.lookupAttributes(PATTERN_FILL_ATTRIBUTES);\n\n this.meshCache = new GLMeshCache(this.gl);\n this.textureCache = new GLTextureCache(this.gl);\n this.imageCache = new GLImageCache(this.gl, this.imageMinification);\n this.gradRamps = new GradientRampAtlas(this.gl);\n this.uploadQuadGeometry();\n this.drawBatch = new DrawBatch(this.gl, this.batchFill);\n this.whiteTexture = createWhiteTexture(this.gl);\n }\n\n private uploadQuadGeometry(): void {\n const gl = this.gl;\n this.quadVbo = gl.createBuffer();\n this.quadIbo = gl.createBuffer();\n if (!this.quadVbo || !this.quadIbo) {\n throw new Error('WeaselRenderer: failed to create quad geometry buffers');\n }\n gl.bindBuffer(gl.ARRAY_BUFFER, this.quadVbo);\n gl.bufferData(gl.ARRAY_BUFFER, QUAD_VERTICES, gl.STATIC_DRAW);\n gl.bindBuffer(gl.ARRAY_BUFFER, null);\n\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.quadIbo);\n gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, QUAD_INDICES, gl.STATIC_DRAW);\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);\n }\n\n /**\n * Compile a consumer-registered shader program against this renderer's GL context.\n *\n * Call once per renderer after the module-level `registerProgram()`.\n * Throws `ShaderCompileError` if compilation fails.\n *\n * In dev mode, calling again with the same handle replaces the compiled program.\n *\n * @experimental\n */\n registerProgram(handle: ShaderProgramHandle): void {\n // A disposed renderer must not compile programs into the cleared registry.\n if (this.disposed) return;\n const src = getProgramSource(handle.id);\n if (!src) {\n throw new Error(\n `WeaselRenderer.registerProgram: program \"${handle.id}\" not found in source registry. ` +\n `Call the module-level registerProgram() first.`,\n );\n }\n this.installProgram(handle.id, src, compileCustomProgram(this.gl, src));\n }\n\n private installProgram(id: string, src: ProgramSource, program: ShaderProgram): void {\n const previous = this.programRegistry.get(id);\n if (previous) this.gl.deleteProgram(previous.handle);\n this.programRegistry.set(id, program);\n this.programSources.set(id, src);\n }\n\n /** Recompile every program whose module-level source was re-registered\n * since it was compiled. A source that fails keeps the old program drawing. */\n private refreshPrograms(): void {\n for (const [id, compiled] of this.programSources) {\n const src = getProgramSource(id);\n if (!src || src === compiled) continue;\n if (src.vert === compiled.vert && src.frag === compiled.frag) {\n this.programSources.set(id, src);\n continue;\n }\n try {\n this.installProgram(id, src, compileCustomProgram(this.gl, src));\n } catch (e) {\n this.programSources.set(id, src);\n console.error(`weasel: failed to recompile re-registered program \"${id}\"; still drawing the previous one:`, e);\n }\n }\n }\n\n /**\n * The compiled program for `id`, compiling it against this context on first\n * use. Unlike {@link registerProgram} this neither throws on a missing\n * source nor on a compile failure: a paint kind asking for a program it\n * never registered must decline the paint, not take down the frame.\n */\n private ensureProgram(id: string): ShaderProgram | null {\n const existing = this.programRegistry.get(id);\n if (existing) return existing;\n if (this.disposed || !getProgramSource(id)) return null;\n try {\n this.registerProgram({ id });\n } catch (e) {\n console.error(`weasel: failed to compile paint program \"${id}\":`, e);\n return null;\n }\n return this.programRegistry.get(id) ?? null;\n }\n\n private glyphPaintProgram(kind: GlyphPaintKind): ShaderProgram {\n const existing = this.glyphPaintPrograms.get(kind);\n if (existing) return existing;\n const [frag, uniforms] = GLYPH_PAINT_SOURCES[kind];\n const program = new ShaderProgram(this.gl, GLYPH_PAINT_VERT_SRC, frag);\n program.lookupUniforms(uniforms);\n program.lookupAttributes(GLYPH_PAINT_ATTRIBUTES);\n this.glyphPaintPrograms.set(kind, program);\n return program;\n }\n\n /** The blend, depth, cull, clear-color, color-mask, VAO, and unpack-flip\n * state every frame assumes.\n * Applied per `render()` rather than once at construction because a\n * co-tenant sharing this context moves all of it between our frames. */\n private applyGlState(): void {\n const gl = this.gl;\n gl.enable(gl.BLEND);\n gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);\n gl.disable(gl.DEPTH_TEST);\n gl.disable(gl.CULL_FACE);\n gl.clearColor(0, 0, 0, 0);\n gl.colorMask(true, true, true, true);\n gl.bindVertexArray(null);\n gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n }\n\n /** Viewport and scissor for this frame. Re-applied inside `render()` because\n * a co-tenant on the same context moves both between our frames. */\n /** This renderer's rect in device pixels, top-left origin. A tile rounds its\n * edges rather than its size, so two that share a CSS edge share a device one. */\n private deviceRect(): { x: number; y: number; width: number; height: number } {\n const ox = this.target?.origin.x ?? 0;\n const oy = this.target?.origin.y ?? 0;\n const x = Math.round(ox * this.dpr);\n const y = Math.round(oy * this.dpr);\n return {\n x,\n y,\n width: Math.round((ox + this.widthCss) * this.dpr) - x,\n height: Math.round((oy + this.heightCss) * this.dpr) - y,\n };\n }\n\n private applyTarget(): void {\n const gl = this.gl;\n const { x, y, width, height } = this.deviceRect();\n if (!this.target) {\n gl.disable(gl.SCISSOR_TEST);\n gl.viewport(0, 0, width, height);\n return;\n }\n const glY = gl.drawingBufferHeight - y - height;\n gl.viewport(x, glY, width, height);\n gl.scissor(x, glY, width, height);\n gl.enable(gl.SCISSOR_TEST);\n }\n\n /** Confine this renderer to a rect of its drawing buffer, or pass null to\n * give it the whole buffer back. Takes effect from the next `render()`. */\n setTarget(target: RenderTarget | null): void {\n this.target = target;\n this.applyTarget();\n }\n\n getTarget(): RenderTarget | null {\n return this.target;\n }\n\n isContextLost(): boolean {\n return this.contextLost;\n }\n\n private onContextLost(e: Event): void {\n e.preventDefault();\n this.contextLost = true;\n }\n\n private onContextRestored(): void {\n this.contextLost = false;\n this.applyGlState();\n this.applyTarget();\n // Every framebuffer, texture and renderbuffer the pool held died with the\n // context. `releaseAll` deletes handles that are already gone, which is a\n // no-op, and drops the bookkeeping that would otherwise hand a dead FBO to\n // the next group with effects.\n this.effectTargets.releaseAll();\n this.paintScratch.release();\n this.paintResources.release();\n this.paintResources = createPaintResources();\n this.pathFill = new ShaderProgram(this.gl, VERT_SRC, FRAG_SRC);\n this.pathFill.lookupUniforms(PATH_FILL_UNIFORMS);\n this.pathFill.lookupAttributes(PATH_FILL_ATTRIBUTES);\n this.pathFillVColor = new ShaderProgram(this.gl, VCOLOR_VERT_SRC, VCOLOR_FRAG_SRC);\n this.pathFillVColor.lookupUniforms(PATH_FILL_UNIFORMS);\n this.pathFillVColor.lookupAttributes(PATH_FILL_VCOLOR_ATTRIBUTES);\n\n this.batchFill = new ShaderProgram(this.gl, BATCH_VERT_SRC, BATCH_FRAG_SRC);\n this.batchFill.lookupUniforms(BATCH_FILL_UNIFORMS);\n this.batchFill.lookupAttributes(BATCH_FILL_ATTRIBUTES);\n this.gradFill = new ShaderProgram(this.gl, GRAD_VERT_SRC, GRAD_FRAG_SRC);\n this.gradFill.lookupUniforms(GRAD_FILL_UNIFORMS);\n this.gradFill.lookupAttributes(GRAD_FILL_ATTRIBUTES);\n\n this.patternFill = new ShaderProgram(this.gl, PATTERN_VERT_SRC, PATTERN_FRAG_SRC);\n this.patternFill.lookupUniforms(PATTERN_FILL_UNIFORMS);\n this.patternFill.lookupAttributes(PATTERN_FILL_ATTRIBUTES);\n this.glyphPaintPrograms.clear();\n this.meshCache = new GLMeshCache(this.gl);\n this.textureCache = new GLTextureCache(this.gl);\n this.imageCache.abandon();\n this.imageCache = new GLImageCache(this.gl, this.imageMinification);\n this.gradRamps = new GradientRampAtlas(this.gl);\n markAllFontsNotUploaded();\n\n this.uploadQuadGeometry();\n this.drawBatch = new DrawBatch(this.gl, this.batchFill);\n this.whiteTexture = createWhiteTexture(this.gl);\n for (const id of this.programRegistry.keys()) {\n const src = getProgramSource(id);\n if (!src) continue;\n try {\n this.programRegistry.set(id, compileCustomProgram(this.gl, src));\n this.programSources.set(id, src);\n } catch (e) {\n console.error(`weasel: failed to recompile program \"${id}\" after context restore:`, e);\n }\n }\n }\n\n /** Free every GL resource this renderer created and detach its\n * context-loss listeners. Idempotent.\n *\n * That covers the built-in and consumer-registered programs (their shaders\n * go with them), the shared quad geometry, the batch, the effect targets,\n * and every cache: atlas and gradient-ramp textures, bitmap and pattern\n * textures, and meshes — transient, persistent, and those whose Path was\n * already collected. The context itself is left alone, so a caller-owned\n * context can host renderer after renderer without accumulating anything.\n *\n * A disposed renderer ignores further `render()` and `registerProgram()`\n * calls. */\n dispose(): void {\n if (this.disposed) return;\n this.disposed = true;\n const gl = this.gl;\n if (this.lossTarget) {\n this.lossTarget.removeEventListener('webglcontextlost', this.boundOnLost);\n this.lossTarget.removeEventListener('webglcontextrestored', this.boundOnRestored);\n }\n this.meshCache.dispose();\n this.paintResources.release();\n for (const prog of [\n this.pathFill, this.pathFillVColor, this.batchFill, this.gradFill, this.patternFill,\n ...this.glyphPaintPrograms.values(),\n ]) {\n gl.deleteProgram(prog.handle);\n }\n this.glyphPaintPrograms.clear();\n for (const prog of this.programRegistry.values()) {\n gl.deleteProgram(prog.handle);\n }\n this.programRegistry.clear();\n this.programSources.clear();\n this.textureCache.free();\n this.imageCache.dispose();\n this.gradRamps.free();\n if (this.quadVbo) gl.deleteBuffer(this.quadVbo);\n if (this.quadIbo) gl.deleteBuffer(this.quadIbo);\n this.drawBatch.dispose();\n if (this.whiteTexture) gl.deleteTexture(this.whiteTexture);\n this.whiteTexture = null;\n this.effectTargets.releaseAll();\n this.paintScratch.release();\n }\n\n /**\n * Draw one frame.\n *\n * `viewMatrix` is the frame's world→screen transform. It is only consulted\n * by `units: 'world'` gradients, which fall back to screen space without\n * it — every other command carries its own transform in the stream, so\n * callers with no view concept can keep calling `render(commands)`.\n */\n render(commands: DrawCommand[], viewMatrix?: GlMat3, opts?: RenderOptions): void {\n if (this.contextLost || this.disposed) return;\n const t0 = now();\n const counters = { drawCalls: 0 };\n const spanStats: RenderSpanStats[] = [];\n this.stats = { drawCalls: 0, ms: 0, spans: spanStats };\n const gl = this.gl;\n // Free GL resources whose Mesh was GC'd since the last frame. Done here\n // (top of render, before any draws) because GL state is known clean —\n // no VAO bound, no draw in flight. Deleting from the FinalizationRegistry\n // callback directly was racy and caused mid-draw crashes.\n this.meshCache.drainPendingDeletes();\n this.imageCache.drainPendingDeletes();\n this.textureCache.sweep(textureRegistry);\n this.refreshPrograms();\n // New frame: refill the dynamic-glyph synchronous bake budget.\n resetBakeBudget(this.bakeBudget);\n // A frame that threw part-way never unwound: its group stack, its staged\n // vertices, the effect buffer it was drawing into, and whichever\n // offscreen buffer it had bound are all still here.\n this.groupState.reset();\n this.drawBatch.reset();\n this.effectTargets.reclaim();\n gl.bindFramebuffer(gl.FRAMEBUFFER, null);\n this.applyGlState();\n this.applyTarget();\n // Ensure all stencil bits are cleared regardless of any mask left over\n // from the previous frame's clip ops.\n if (this.target?.clear !== false) {\n gl.stencilMask(0xFF);\n gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);\n }\n const device = this.deviceRect();\n const ctx: DrawContext = {\n gl,\n pathFill: this.pathFill,\n pathFillVColor: this.pathFillVColor,\n batchFill: this.batchFill,\n gradFill: this.gradFill,\n patternFill: this.patternFill,\n meshCache: this.meshCache,\n textureCache: this.textureCache,\n imageCache: this.imageCache,\n gradRamps: this.gradRamps,\n programRegistry: this.programRegistry,\n ensureProgram: (id) => this.ensureProgram(id),\n glyphPaintProgram: (kind) => this.glyphPaintProgram(kind),\n quadVbo: this.quadVbo,\n quadIbo: this.quadIbo,\n drawBatch: this.drawBatch,\n whiteTexture: this.whiteTexture,\n state: this.groupState,\n widthCss: this.widthCss,\n heightCss: this.heightCss,\n clipDepth: 0,\n clipStack: [],\n flattenTolerance: this.flattenTolerance,\n textOutlineMinScreenSize: this.textOutlineMinScreenSize,\n viewMatrix,\n effectTargets: this.effectTargets,\n paintScratch: this.paintScratch,\n renderTarget: null,\n deviceWidth: device.width,\n deviceHeight: device.height,\n restoreTargetRect: () => this.applyTarget(),\n paintResources: this.paintResources,\n stats: counters,\n };\n // A frame that threw part-way left its clip here.\n this.drawBatch.clip = null;\n const spans = opts?.spans ?? [];\n let next = 0;\n for (let si = 0; si < spans.length; si++) {\n const span = spans[si];\n for (; next < span.start; next++) dispatch(ctx, commands[next]);\n const drawsBefore = counters.drawCalls;\n const spanT0 = now();\n for (; next < span.end; next++) dispatch(ctx, commands[next]);\n // The stream's closing flush belongs to whatever it closes.\n if (si === spans.length - 1 && next >= commands.length) flushBatch(ctx);\n spanStats.push({ id: span.id, drawCalls: counters.drawCalls - drawsBefore, ms: now() - spanT0 });\n }\n for (; next < commands.length; next++) dispatch(ctx, commands[next]);\n // The stream ended, so whatever is still staged has nothing left that\n // could merge with it.\n flushBatch(ctx);\n // Free transient resources allocated during this frame (e.g. per-frame\n // stroke ribbons from tessellateStroke). Done after all draws complete\n // so we never delete a buffer that's still bound to a pending draw.\n this.meshCache.freeTransient();\n this.stats.drawCalls = counters.drawCalls;\n this.stats.ms = now() - t0;\n }\n\n /** What the last `render()` cost: GL draw calls issued and CPU milliseconds\n * spent issuing them, in total and per requested span. GPU time is not\n * measured; the GPU runs asynchronously. */\n lastFrameStats(): RenderStats {\n return this.stats;\n }\n\n /** Change the curve-flattening tolerance after construction. `undefined`\n * restores the cached `DEFAULT_FLATTEN_TOLERANCE` route. */\n setFlattenTolerance(tolerance: number | undefined): void {\n this.flattenTolerance = tolerance;\n }\n\n resize(dims: { width: number; height: number; dpr: number }): void {\n this.widthCss = dims.width;\n this.heightCss = dims.height;\n this.dpr = dims.dpr;\n if (this.canvas) {\n this.canvas.width = Math.round(dims.width * dims.dpr);\n this.canvas.height = Math.round(dims.height * dims.dpr);\n if (this.canvas.style) {\n this.canvas.style.width = `${dims.width}px`;\n this.canvas.style.height = `${dims.height}px`;\n }\n }\n this.applyTarget();\n }\n\n /** @internal */ _gl(): WebGL2RenderingContext { return this.gl; }\n /** @internal */ _pathFill(): ShaderProgram { return this.pathFill; }\n /** @internal */ _pathFillVColor(): ShaderProgram { return this.pathFillVColor; }\n /** @internal */ _drawBatch(): DrawBatch { return this.drawBatch; }\n /** @internal */ _batchFill(): ShaderProgram { return this.batchFill; }\n /** @internal */ _gradFill(): ShaderProgram { return this.gradFill; }\n /** @internal */ _patternFill(): ShaderProgram { return this.patternFill; }\n /** @internal */ _meshCache(): GLMeshCache { return this.meshCache; }\n /** @internal */ _textureCache(): GLTextureCache { return this.textureCache; }\n /** @internal */ _imageCache(): GLImageCache { return this.imageCache; }\n /** @internal */ _gradRamps(): GradientRampAtlas { return this.gradRamps; }\n /** @internal */ _groupState(): GroupState { return this.groupState; }\n /** @internal */ _widthCss(): number { return this.widthCss; }\n /** @internal */ _heightCss(): number { return this.heightCss; }\n /** @internal */ _dpr(): number { return this.dpr; }\n}\n\n/** The 1x1 opaque white texture a batch flush binds when its run samples no\n * image. NEAREST both ways: one texel, so there is nothing to filter, and a\n * mipmap filter on a texture with no mipmaps is incomplete. */\nfunction createWhiteTexture(gl: WebGL2RenderingContext): WebGLTexture | null {\n const tex = gl.createTexture();\n if (!tex) return null;\n gl.bindTexture(gl.TEXTURE_2D, tex);\n gl.texImage2D(\n gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,\n new Uint8Array([255, 255, 255, 255]),\n );\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n gl.bindTexture(gl.TEXTURE_2D, null);\n return tex;\n}\n"],"mappings":"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"}
|
|
@@ -15,21 +15,31 @@ var i = class {
|
|
|
15
15
|
map = /* @__PURE__ */ new WeakMap();
|
|
16
16
|
textures = /* @__PURE__ */ new Set();
|
|
17
17
|
magFilters = /* @__PURE__ */ new WeakMap();
|
|
18
|
+
finalizer;
|
|
19
|
+
pendingDeletes = [];
|
|
18
20
|
constructor(n, r = "linear") {
|
|
19
21
|
this.gl = n, this.minification = r;
|
|
20
22
|
let i = n.getParameter(n.MAX_TEXTURE_SIZE);
|
|
21
|
-
this.maxTextureSide = typeof i == "number" && i > 0 ? i : e,
|
|
23
|
+
this.maxTextureSide = typeof i == "number" && i > 0 ? i : e, this.finalizer = new FinalizationRegistry((e) => {
|
|
24
|
+
this.textures.delete(e) && this.pendingDeletes.push(e);
|
|
25
|
+
}), t.add(this);
|
|
26
|
+
}
|
|
27
|
+
drainPendingDeletes() {
|
|
28
|
+
if (this.pendingDeletes.length !== 0) {
|
|
29
|
+
for (let e of this.pendingDeletes) this.gl.deleteTexture(e);
|
|
30
|
+
this.pendingDeletes.length = 0;
|
|
31
|
+
}
|
|
22
32
|
}
|
|
23
33
|
release(e) {
|
|
24
34
|
let t = this.map.get(e);
|
|
25
|
-
t && (this.gl.deleteTexture(t), this.textures.delete(t), this.map.delete(e), this.magFilters.delete(e));
|
|
35
|
+
t && (this.gl.deleteTexture(t), this.textures.delete(t), this.finalizer.unregister(e), this.map.delete(e), this.magFilters.delete(e));
|
|
26
36
|
}
|
|
27
37
|
dispose() {
|
|
28
38
|
for (let e of this.textures) this.gl.deleteTexture(e);
|
|
29
|
-
this.abandon();
|
|
39
|
+
this.drainPendingDeletes(), this.abandon();
|
|
30
40
|
}
|
|
31
41
|
abandon() {
|
|
32
|
-
this.textures.clear(), t.delete(this);
|
|
42
|
+
this.textures.clear(), this.pendingDeletes.length = 0, t.delete(this);
|
|
33
43
|
}
|
|
34
44
|
upload(e, t, n) {
|
|
35
45
|
let r = this.map.get(e);
|
|
@@ -38,7 +48,7 @@ var i = class {
|
|
|
38
48
|
if (!o) throw Error("GLImageCache: createTexture failed");
|
|
39
49
|
i.bindTexture(i.TEXTURE_2D, o), i.texImage2D(i.TEXTURE_2D, 0, i.RGBA, i.RGBA, i.UNSIGNED_BYTE, t), i.texParameteri(i.TEXTURE_2D, i.TEXTURE_MIN_FILTER, this.minification === "mipmap" ? i.LINEAR_MIPMAP_LINEAR : i.LINEAR), i.texParameteri(i.TEXTURE_2D, i.TEXTURE_MAG_FILTER, i.LINEAR), this.magFilters.set(e, i.LINEAR);
|
|
40
50
|
let [s, c] = a(i, n);
|
|
41
|
-
return i.texParameteri(i.TEXTURE_2D, i.TEXTURE_WRAP_S, s), i.texParameteri(i.TEXTURE_2D, i.TEXTURE_WRAP_T, c), this.minification === "mipmap" && i.generateMipmap(i.TEXTURE_2D), i.bindTexture(i.TEXTURE_2D, null), this.map.set(e, o), this.textures.add(o), o;
|
|
51
|
+
return i.texParameteri(i.TEXTURE_2D, i.TEXTURE_WRAP_S, s), i.texParameteri(i.TEXTURE_2D, i.TEXTURE_WRAP_T, c), this.minification === "mipmap" && i.generateMipmap(i.TEXTURE_2D), i.bindTexture(i.TEXTURE_2D, null), this.map.set(e, o), this.textures.add(o), this.finalizer.register(e, o, e), o;
|
|
42
52
|
}
|
|
43
53
|
bind(e, t) {
|
|
44
54
|
let n = this.map.get(e);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GLImageCache.js","names":["DEFAULT_MAX_TEXTURE_SIDE","live","releaseImageSource","cache","key","maxImageTextureSide","side","GLImageCache","gl","minification","reported","tex","existing","source","wrapS","wrapT","wrapModes","repetition","unit","filter","rep"],"sources":["../../../src/renderer/cache/GLImageCache.ts"],"sourcesContent":["/**\n * GL texture upload cache for ImageBitmap objects.\n *\n * Key: ImageBitmap (or pattern source object) identity (WeakMap) — lets GC\n * reclaim unreferenced bitmaps. The GL textures are NOT freed when the source\n * is gc'd; a source's owner that knows it will never be drawn again frees them\n * with {@link releaseImageSource}, and `dispose` frees them all.\n *\n * Wrapping is set once at upload time per the `repetition` parameter:\n * - undefined / 'no-repeat' → CLAMP_TO_EDGE\n * - 'repeat' → REPEAT (both axes)\n * - 'repeat-x' → REPEAT on S, CLAMP on T\n * - 'repeat-y' → CLAMP on S, REPEAT on T\n *\n * Convention §2: texels stored straight; shader premultiplies.\n */\n\nexport type PatternRepetition = 'repeat' | 'repeat-x' | 'repeat-y' | 'no-repeat';\n\n/** MIN_FILTER strategy for uploaded textures. */\nexport type ImageMinification = 'linear' | 'mipmap';\n\ntype TexSource = ImageBitmap | ImageData | HTMLCanvasElement | HTMLImageElement;\n\n/** Assumed when no live context has reported its `MAX_TEXTURE_SIZE`. Above\n * WebGL2's guaranteed 2048, and at or below what every current GPU reports. */\nconst DEFAULT_MAX_TEXTURE_SIDE = 4096;\n\nconst live = new Set<GLImageCache>();\n\n/** Free `key`'s texture in every live renderer. For an owner that has replaced\n * a source and will never draw the old one again — a later draw of it simply\n * re-uploads. */\nexport function releaseImageSource(key: object): void {\n for (const cache of live) cache.release(key);\n}\n\n/** The largest texture side every live renderer can hold: the smallest\n * `MAX_TEXTURE_SIZE` among them, or a conservative default when none is live. */\nexport function maxImageTextureSide(): number {\n let side = Infinity;\n for (const cache of live) side = Math.min(side, cache.maxTextureSide);\n return Number.isFinite(side) ? side : DEFAULT_MAX_TEXTURE_SIDE;\n}\n\nexport class GLImageCache {\n /** This context's `MAX_TEXTURE_SIZE`. */\n readonly maxTextureSide: number;\n\n private readonly map = new WeakMap<object, WebGLTexture>();\n /** Every texture `map` holds, so `dispose` can reach them — a WeakMap\n * cannot be enumerated. */\n private readonly textures = new Set<WebGLTexture>();\n /**\n * MAG_FILTER each texture currently carries, so a redundant write can be\n * skipped.\n *\n * The batch sets this per flush — the same bitmap can be drawn at both\n * filters in one frame, and the value has to be right at the draw rather than\n * at upload. Filtering is state on the *texture object*, though, not on the\n * unit, so re-asserting a value it already has is a write to a live texture\n * for no reason. On a large sheet that is not free: a consumer's wall samples\n * a 5652px-square atlas, 122MB resident, and measured `sampling: 'nearest'`\n * costing up to 8x `'linear'` there — where the linear pass re-asserts the\n * upload default and the nearest pass changes state on every draw.\n *\n * Whether that is the cause is unproven (see `docs/TODO.md`), but the write\n * was redundant either way.\n */\n private readonly magFilters = new WeakMap<object, number>();\n\n /** `minification` selects the MIN_FILTER strategy for uploaded textures.\n * `'linear'` (default) is the screen path's existing behavior. `'mipmap'`\n * generates mipmaps and filters LINEAR_MIPMAP_LINEAR — required for\n * quality minification when a large source bitmap is drawn small (the\n * headless print/export path); bilinear-only minification undersamples\n * and produces moiré. */\n constructor(\n private readonly gl: WebGL2RenderingContext,\n private readonly minification: ImageMinification = 'linear',\n ) {\n const reported: unknown = gl.getParameter(gl.MAX_TEXTURE_SIZE);\n this.maxTextureSide = typeof reported === 'number' && reported > 0\n ? reported\n : DEFAULT_MAX_TEXTURE_SIDE;\n live.add(this);\n }\n\n /** Delete `key`'s texture, if it has one. */\n release(key: object): void {\n const tex = this.map.get(key);\n if (!tex) return;\n this.gl.deleteTexture(tex);\n this.textures.delete(tex);\n this.map.delete(key);\n this.magFilters.delete(key);\n }\n\n /** Delete every texture this cache uploaded and stop answering\n * {@link releaseImageSource} and {@link maxImageTextureSide}. Idempotent. */\n dispose(): void {\n for (const tex of this.textures) this.gl.deleteTexture(tex);\n this.abandon();\n }\n\n /** {@link dispose} without the deletes, for a cache whose context was lost:\n * its textures died with it. */\n abandon(): void {\n this.textures.clear();\n live.delete(this);\n }\n\n upload(\n key: object,\n source: TexSource,\n repetition?: PatternRepetition,\n ): WebGLTexture {\n const existing = this.map.get(key);\n if (existing) return existing;\n\n const gl = this.gl;\n const tex = gl.createTexture();\n if (!tex) throw new Error('GLImageCache: createTexture failed');\n\n gl.bindTexture(gl.TEXTURE_2D, tex);\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source as TexImageSource);\n gl.texParameteri(\n gl.TEXTURE_2D,\n gl.TEXTURE_MIN_FILTER,\n this.minification === 'mipmap' ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR,\n );\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n this.magFilters.set(key, gl.LINEAR);\n\n const [wrapS, wrapT] = wrapModes(gl, repetition);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT);\n if (this.minification === 'mipmap') gl.generateMipmap(gl.TEXTURE_2D);\n gl.bindTexture(gl.TEXTURE_2D, null);\n\n this.map.set(key, tex);\n this.textures.add(tex);\n return tex;\n }\n\n bind(key: object, unit: number): void {\n const tex = this.map.get(key);\n if (!tex) throw new Error('GLImageCache: image not uploaded; call upload() first');\n const gl = this.gl;\n gl.activeTexture(gl.TEXTURE0 + unit);\n gl.bindTexture(gl.TEXTURE_2D, tex);\n }\n\n /** Set `key`'s MAG_FILTER, skipping the call when it already holds that\n * value. Its texture must be bound to the active unit — callers pair this\n * with `bind`. */\n setMagFilter(key: object, filter: number): void {\n if (this.magFilters.get(key) === filter) return;\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, filter);\n this.magFilters.set(key, filter);\n }\n}\n\nfunction wrapModes(\n gl: WebGL2RenderingContext,\n rep?: PatternRepetition,\n): [number, number] {\n switch (rep) {\n case 'repeat': return [gl.REPEAT, gl.REPEAT];\n case 'repeat-x': return [gl.REPEAT, gl.CLAMP_TO_EDGE];\n case 'repeat-y': return [gl.CLAMP_TO_EDGE, gl.REPEAT];\n default: return [gl.CLAMP_TO_EDGE, gl.CLAMP_TO_EDGE];\n }\n}\n"],"mappings":";AA0BA,IAAMA,IAA2B,MAE3BC,oBAAO,IAAI,IAAkB;AAKnC,SAAgBC,EAAmB,GAAmB;CACpD,KAAK,IAAMC,KAASF,GAAM,EAAM,QAAQG,CAAG;AAC7C;AAIA,SAAgBC,IAA8B;CAC5C,IAAIC,IAAO;CACX,KAAK,IAAMH,KAASF,GAAM,IAAO,KAAK,IAAIK,GAAMH,EAAM,cAAc;CACpE,OAAO,OAAO,SAASG,CAAI,IAAIA,IAAON;AACxC;AAEA,IAAaO,IAAb,MAA0B;CAiCL;CACA;CAhCnB;CAEA,sBAAuB,IAAI,QAA8B;CAGzD,2BAA4B,IAAI,IAAkB;CAiBlD,6BAA8B,IAAI,QAAwB;CAQ1D,YACE,GACA,IAAmD,UACnD;EADiB,AADA,KAAA,KAAAC,GACA,KAAA,eAAAC;EAEjB,IAAMC,IAAoBF,EAAG,aAAaA,EAAG,gBAAgB;EAI7D,AAHA,KAAK,iBAAiB,OAAOE,KAAa,YAAYA,IAAW,IAC7DA,IACAV,GACJC,EAAK,IAAI,IAAI;CACf;CAGA,QAAQ,GAAmB;EACzB,IAAMU,IAAM,KAAK,IAAI,IAAIP,CAAG;EAC5B,AAAKO,MACL,KAAK,GAAG,cAAcA,CAAG,GACzB,KAAK,SAAS,OAAOA,CAAG,GACxB,KAAK,IAAI,OAAOP,CAAG,GACnB,KAAK,WAAW,OAAOA,CAAG;CAC5B;CAIA,UAAgB;EACd,KAAK,IAAMO,KAAO,KAAK,UAAU,KAAK,GAAG,cAAcA,CAAG;EAC1D,KAAK,QAAQ;CACf;CAIA,UAAgB;EAEd,AADA,KAAK,SAAS,MAAM,GACpBV,EAAK,OAAO,IAAI;CAClB;CAEA,OACE,GACA,GACA,GACc;EACd,IAAMW,IAAW,KAAK,IAAI,IAAIR,CAAG;EACjC,IAAIQ,GAAU,OAAOA;EAErB,IAAMJ,IAAK,KAAK,IACVG,IAAMH,EAAG,cAAc;EAC7B,IAAI,CAACG,GAAK,MAAU,MAAM,oCAAoC;EAU9D,AARAH,EAAG,YAAYA,EAAG,YAAYG,CAAG,GACjCH,EAAG,WAAWA,EAAG,YAAY,GAAGA,EAAG,MAAMA,EAAG,MAAMA,EAAG,eAAeK,CAAwB,GAC5FL,EAAG,cACDA,EAAG,YACHA,EAAG,oBACH,KAAK,iBAAiB,WAAWA,EAAG,uBAAuBA,EAAG,MAChE,GACAA,EAAG,cAAcA,EAAG,YAAYA,EAAG,oBAAoBA,EAAG,MAAM,GAChE,KAAK,WAAW,IAAIJ,GAAKI,EAAG,MAAM;EAElC,IAAM,CAACM,GAAOC,KAASC,EAAUR,GAAIS,CAAU;EAQ/C,OAPAT,EAAG,cAAcA,EAAG,YAAYA,EAAG,gBAAgBM,CAAK,GACxDN,EAAG,cAAcA,EAAG,YAAYA,EAAG,gBAAgBO,CAAK,GACpD,KAAK,iBAAiB,YAAUP,EAAG,eAAeA,EAAG,UAAU,GACnEA,EAAG,YAAYA,EAAG,YAAY,IAAI,GAElC,KAAK,IAAI,IAAIJ,GAAKO,CAAG,GACrB,KAAK,SAAS,IAAIA,CAAG,GACdA;CACT;CAEA,KAAK,GAAa,GAAoB;EACpC,IAAMA,IAAM,KAAK,IAAI,IAAIP,CAAG;EAC5B,IAAI,CAACO,GAAK,MAAU,MAAM,uDAAuD;EACjF,IAAMH,IAAK,KAAK;EAEhB,AADAA,EAAG,cAAcA,EAAG,WAAWU,CAAI,GACnCV,EAAG,YAAYA,EAAG,YAAYG,CAAG;CACnC;CAKA,aAAa,GAAa,GAAsB;EAC9C,AAAI,KAAK,WAAW,IAAIP,CAAG,MAAMe,MACjC,KAAK,GAAG,cAAc,KAAK,GAAG,YAAY,KAAK,GAAG,oBAAoBA,CAAM,GAC5E,KAAK,WAAW,IAAIf,GAAKe,CAAM;CACjC;AACF;AAEA,SAASH,EACP,GACA,GACkB;CAClB,QAAQI,GAAR;EACE,KAAK,UAAY,OAAO,CAACZ,EAAG,QAAQA,EAAG,MAAM;EAC7C,KAAK,YAAY,OAAO,CAACA,EAAG,QAAQA,EAAG,aAAa;EACpD,KAAK,YAAY,OAAO,CAACA,EAAG,eAAeA,EAAG,MAAM;EACpD,SAAiB,OAAO,CAACA,EAAG,eAAeA,EAAG,aAAa;CAC7D;AACF"}
|
|
1
|
+
{"version":3,"file":"GLImageCache.js","names":["DEFAULT_MAX_TEXTURE_SIDE","live","releaseImageSource","cache","key","maxImageTextureSide","side","GLImageCache","gl","minification","reported","tex","existing","source","wrapS","wrapT","wrapModes","repetition","unit","filter","rep"],"sources":["../../../src/renderer/cache/GLImageCache.ts"],"sourcesContent":["/**\n * GL texture upload cache for ImageBitmap objects.\n *\n * Key: ImageBitmap (or pattern source object) identity (WeakMap) — lets GC\n * reclaim unreferenced bitmaps. A collected source's texture is queued and\n * deleted at the renderer's next frame, the way `GLMeshCache` frees a mesh's\n * buffers; GC timing is not prompt, so a source's owner that knows it will\n * never be drawn again frees it at once with {@link releaseImageSource}, and\n * `dispose` frees them all.\n *\n * Wrapping is set once at upload time per the `repetition` parameter:\n * - undefined / 'no-repeat' → CLAMP_TO_EDGE\n * - 'repeat' → REPEAT (both axes)\n * - 'repeat-x' → REPEAT on S, CLAMP on T\n * - 'repeat-y' → CLAMP on S, REPEAT on T\n *\n * Convention §2: texels stored straight; shader premultiplies.\n */\n\nexport type PatternRepetition = 'repeat' | 'repeat-x' | 'repeat-y' | 'no-repeat';\n\n/** MIN_FILTER strategy for uploaded textures. */\nexport type ImageMinification = 'linear' | 'mipmap';\n\ntype TexSource = ImageBitmap | ImageData | HTMLCanvasElement | HTMLImageElement;\n\n/** Assumed when no live context has reported its `MAX_TEXTURE_SIZE`. Above\n * WebGL2's guaranteed 2048, and at or below what every current GPU reports. */\nconst DEFAULT_MAX_TEXTURE_SIDE = 4096;\n\nconst live = new Set<GLImageCache>();\n\n/** Free `key`'s texture in every live renderer. For an owner that has replaced\n * a source and will never draw the old one again — a later draw of it simply\n * re-uploads. */\nexport function releaseImageSource(key: object): void {\n for (const cache of live) cache.release(key);\n}\n\n/** The largest texture side every live renderer can hold: the smallest\n * `MAX_TEXTURE_SIZE` among them, or a conservative default when none is live. */\nexport function maxImageTextureSide(): number {\n let side = Infinity;\n for (const cache of live) side = Math.min(side, cache.maxTextureSide);\n return Number.isFinite(side) ? side : DEFAULT_MAX_TEXTURE_SIDE;\n}\n\nexport class GLImageCache {\n /** This context's `MAX_TEXTURE_SIZE`. */\n readonly maxTextureSide: number;\n\n private readonly map = new WeakMap<object, WebGLTexture>();\n /** Every texture `map` holds, so `dispose` can reach them — a WeakMap\n * cannot be enumerated. */\n private readonly textures = new Set<WebGLTexture>();\n /**\n * MAG_FILTER each texture currently carries, so a redundant write can be\n * skipped.\n *\n * The batch sets this per flush — the same bitmap can be drawn at both\n * filters in one frame, and the value has to be right at the draw rather than\n * at upload. Filtering is state on the *texture object*, though, not on the\n * unit, so re-asserting a value it already has is a write to a live texture\n * for no reason. On a large sheet that is not free: a consumer's wall samples\n * a 5652px-square atlas, 122MB resident, and measured `sampling: 'nearest'`\n * costing up to 8x `'linear'` there — where the linear pass re-asserts the\n * upload default and the nearest pass changes state on every draw.\n *\n * Whether that is the cause is unproven (see `docs/TODO.md`), but the write\n * was redundant either way.\n */\n private readonly magFilters = new WeakMap<object, number>();\n private readonly finalizer: FinalizationRegistry<WebGLTexture>;\n private readonly pendingDeletes: WebGLTexture[] = [];\n\n /** `minification` selects the MIN_FILTER strategy for uploaded textures.\n * `'linear'` (default) is the screen path's existing behavior. `'mipmap'`\n * generates mipmaps and filters LINEAR_MIPMAP_LINEAR — required for\n * quality minification when a large source bitmap is drawn small (the\n * headless print/export path); bilinear-only minification undersamples\n * and produces moiré. */\n constructor(\n private readonly gl: WebGL2RenderingContext,\n private readonly minification: ImageMinification = 'linear',\n ) {\n const reported: unknown = gl.getParameter(gl.MAX_TEXTURE_SIZE);\n this.maxTextureSide = typeof reported === 'number' && reported > 0\n ? reported\n : DEFAULT_MAX_TEXTURE_SIDE;\n this.finalizer = new FinalizationRegistry<WebGLTexture>((tex) => {\n if (this.textures.delete(tex)) this.pendingDeletes.push(tex);\n });\n live.add(this);\n }\n\n /** Delete the textures of sources collected since the last call. The\n * renderer calls this at the top of each `render()`. */\n drainPendingDeletes(): void {\n if (this.pendingDeletes.length === 0) return;\n for (const tex of this.pendingDeletes) this.gl.deleteTexture(tex);\n this.pendingDeletes.length = 0;\n }\n\n /** Delete `key`'s texture, if it has one. */\n release(key: object): void {\n const tex = this.map.get(key);\n if (!tex) return;\n this.gl.deleteTexture(tex);\n this.textures.delete(tex);\n this.finalizer.unregister(key);\n this.map.delete(key);\n this.magFilters.delete(key);\n }\n\n /** Delete every texture this cache uploaded and stop answering\n * {@link releaseImageSource} and {@link maxImageTextureSide}. Idempotent. */\n dispose(): void {\n for (const tex of this.textures) this.gl.deleteTexture(tex);\n this.drainPendingDeletes();\n this.abandon();\n }\n\n /** {@link dispose} without the deletes, for a cache whose context was lost:\n * its textures died with it. */\n abandon(): void {\n this.textures.clear();\n this.pendingDeletes.length = 0;\n live.delete(this);\n }\n\n upload(\n key: object,\n source: TexSource,\n repetition?: PatternRepetition,\n ): WebGLTexture {\n const existing = this.map.get(key);\n if (existing) return existing;\n\n const gl = this.gl;\n const tex = gl.createTexture();\n if (!tex) throw new Error('GLImageCache: createTexture failed');\n\n gl.bindTexture(gl.TEXTURE_2D, tex);\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source as TexImageSource);\n gl.texParameteri(\n gl.TEXTURE_2D,\n gl.TEXTURE_MIN_FILTER,\n this.minification === 'mipmap' ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR,\n );\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n this.magFilters.set(key, gl.LINEAR);\n\n const [wrapS, wrapT] = wrapModes(gl, repetition);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT);\n if (this.minification === 'mipmap') gl.generateMipmap(gl.TEXTURE_2D);\n gl.bindTexture(gl.TEXTURE_2D, null);\n\n this.map.set(key, tex);\n this.textures.add(tex);\n this.finalizer.register(key, tex, key);\n return tex;\n }\n\n bind(key: object, unit: number): void {\n const tex = this.map.get(key);\n if (!tex) throw new Error('GLImageCache: image not uploaded; call upload() first');\n const gl = this.gl;\n gl.activeTexture(gl.TEXTURE0 + unit);\n gl.bindTexture(gl.TEXTURE_2D, tex);\n }\n\n /** Set `key`'s MAG_FILTER, skipping the call when it already holds that\n * value. Its texture must be bound to the active unit — callers pair this\n * with `bind`. */\n setMagFilter(key: object, filter: number): void {\n if (this.magFilters.get(key) === filter) return;\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, filter);\n this.magFilters.set(key, filter);\n }\n}\n\nfunction wrapModes(\n gl: WebGL2RenderingContext,\n rep?: PatternRepetition,\n): [number, number] {\n switch (rep) {\n case 'repeat': return [gl.REPEAT, gl.REPEAT];\n case 'repeat-x': return [gl.REPEAT, gl.CLAMP_TO_EDGE];\n case 'repeat-y': return [gl.CLAMP_TO_EDGE, gl.REPEAT];\n default: return [gl.CLAMP_TO_EDGE, gl.CLAMP_TO_EDGE];\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { POSITION_ATTRIBUTE_LOCATION as e } from "../shaders/ShaderProgram.js";
|
|
1
2
|
//#region src/renderer/cache/GLMeshCache.ts
|
|
2
|
-
var
|
|
3
|
+
var t = class {
|
|
3
4
|
gl;
|
|
4
|
-
aPositionLoc;
|
|
5
5
|
map = /* @__PURE__ */ new WeakMap();
|
|
6
6
|
seen = /* @__PURE__ */ new WeakSet();
|
|
7
7
|
finalizer;
|
|
@@ -9,8 +9,8 @@ var e = class {
|
|
|
9
9
|
pendingDeletes = [];
|
|
10
10
|
disposed = !1;
|
|
11
11
|
transientThisFrame = [];
|
|
12
|
-
constructor(e
|
|
13
|
-
this.gl = e, this.
|
|
12
|
+
constructor(e) {
|
|
13
|
+
this.gl = e, this.finalizer = new FinalizationRegistry((e) => {
|
|
14
14
|
this.persistent.delete(e) && this.pendingDeletes.push(e);
|
|
15
15
|
});
|
|
16
16
|
}
|
|
@@ -73,30 +73,30 @@ var e = class {
|
|
|
73
73
|
_enqueueDeleteForTest(e) {
|
|
74
74
|
this.pendingDeletes.push(e);
|
|
75
75
|
}
|
|
76
|
-
upload(
|
|
77
|
-
let
|
|
78
|
-
if (!
|
|
79
|
-
|
|
80
|
-
let
|
|
81
|
-
if (!
|
|
82
|
-
|
|
83
|
-
let
|
|
84
|
-
if (!
|
|
85
|
-
return
|
|
76
|
+
upload(t) {
|
|
77
|
+
let n = this.gl, r = n.createVertexArray();
|
|
78
|
+
if (!r) throw Error("createVertexArray returned null");
|
|
79
|
+
n.bindVertexArray(r);
|
|
80
|
+
let i = n.createBuffer();
|
|
81
|
+
if (!i) throw Error("createBuffer (VBO) returned null");
|
|
82
|
+
n.bindBuffer(n.ARRAY_BUFFER, i), n.bufferData(n.ARRAY_BUFFER, t.vertices, n.STATIC_DRAW), n.enableVertexAttribArray(e), n.vertexAttribPointer(e, 2, n.FLOAT, !1, 0, 0);
|
|
83
|
+
let a = n.createBuffer();
|
|
84
|
+
if (!a) throw Error("createBuffer (IBO) returned null");
|
|
85
|
+
return n.bindBuffer(n.ELEMENT_ARRAY_BUFFER, a), n.bufferData(n.ELEMENT_ARRAY_BUFFER, t.indices, n.STATIC_DRAW), n.bindVertexArray(null), {
|
|
86
86
|
handle: {
|
|
87
|
-
vao:
|
|
88
|
-
indexCount:
|
|
89
|
-
requiresStencil:
|
|
90
|
-
anchorA:
|
|
91
|
-
anchorB:
|
|
92
|
-
anchorT:
|
|
87
|
+
vao: r,
|
|
88
|
+
indexCount: t.indices.length,
|
|
89
|
+
requiresStencil: t.requiresStencil ?? !1,
|
|
90
|
+
anchorA: t.anchorA,
|
|
91
|
+
anchorB: t.anchorB,
|
|
92
|
+
anchorT: t.anchorT
|
|
93
93
|
},
|
|
94
|
-
vbo:
|
|
95
|
-
ibo:
|
|
94
|
+
vbo: i,
|
|
95
|
+
ibo: a
|
|
96
96
|
};
|
|
97
97
|
}
|
|
98
98
|
};
|
|
99
99
|
//#endregion
|
|
100
|
-
export {
|
|
100
|
+
export { t as GLMeshCache };
|
|
101
101
|
|
|
102
102
|
//# sourceMappingURL=GLMeshCache.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GLMeshCache.js","names":["GLMeshCache","gl","aPositionLoc","resources","cached","mesh","handle","r","vao","vbo","ibo"],"sources":["../../../src/renderer/cache/GLMeshCache.ts"],"sourcesContent":["import type { Mesh } from '@weasel-js/geom/tessellate';\n\nexport interface GLMeshHandle {\n readonly vao: WebGLVertexArrayObject;\n readonly indexCount: number;\n readonly requiresStencil: boolean;\n readonly anchorA?: Uint32Array;\n readonly anchorB?: Uint32Array;\n readonly anchorT?: Float32Array;\n}\n\n/** Resources to release when a Mesh is reclaimed by GC. */\ninterface MeshResources {\n vao: WebGLVertexArrayObject;\n vbo: WebGLBuffer;\n ibo: WebGLBuffer;\n}\n\n/**\n * Caches GL-side buffers + VAO per `Mesh` identity. Upload happens lazily\n * on first `handleFor(mesh)` call.\n *\n * **GC-aware cleanup (deferred-delete queue):** when a Mesh becomes\n * unreachable, the WeakMap entry is dropped automatically — but the\n * underlying GL resources (VAO, VBO, IBO) would leak forever without an\n * explicit `gl.delete*` call. We register each Mesh with a\n * `FinalizationRegistry`; when the Mesh is reclaimed, the finalizer pushes\n * the resources onto an internal queue. The renderer drains this queue at\n * the start of each `render()` call (via `drainPendingDeletes()`), when GL\n * is in a known state — no VAO bound, no draw mid-flight. Deleting from the\n * finalizer directly was racy (it could fire between two `gl.bindVertexArray`\n * calls inside `dispatch()`, sometimes corrupting the next draw); deferring\n * the actual delete to a known-safe point keeps the use-after-free risk to\n * zero.\n *\n * Caveats:\n * - Finalizer timing is non-deterministic; resources may live for a frame\n * or two beyond their Mesh, but they will be reclaimed.\n * - If the GL context is lost between finalizer and drain, the drain\n * no-ops safely (`gl.isContextLost()` makes deletes no-ops, and\n * `gl.deleteBuffer(null)` is allowed).\n *\n * The cache is GL-context-bound; if the context is lost and re-created, the\n * renderer should construct a new GLMeshCache. (Context loss handling lives\n * in `WeaselRenderer`.)\n */\nexport class GLMeshCache {\n private readonly map = new WeakMap<Mesh, GLMeshHandle>();\n /** Meshes this context has drawn at least once; see `uploadRecurring`. */\n private readonly seen = new WeakSet<Mesh>();\n private readonly finalizer: FinalizationRegistry<MeshResources>;\n /** Every persistent upload not yet freed, so `dispose` can reach them. */\n private readonly persistent = new Set<MeshResources>();\n private readonly pendingDeletes: MeshResources[] = [];\n private disposed = false;\n /** Transient resources allocated this frame; freed at end of render(). */\n private readonly transientThisFrame: MeshResources[] = [];\n\n constructor(\n private readonly gl: WebGL2RenderingContext,\n private readonly aPositionLoc: number,\n ) {\n this.finalizer = new FinalizationRegistry<MeshResources>((resources) => {\n // Defer the actual gl.delete* until the renderer's next render(),\n // when no VAO/buffer is bound mid-draw. Direct deletion from the\n // finalizer caused use-after-free crashes when GC fired between\n // bindVertexArray and drawElements inside dispatch().\n if (!this.persistent.delete(resources)) return;\n this.pendingDeletes.push(resources);\n });\n }\n\n handleFor(mesh: Mesh): GLMeshHandle {\n const cached = this.map.get(mesh);\n if (cached) return cached;\n const { handle, vbo, ibo } = this.upload(mesh);\n this.map.set(mesh, handle);\n const resources = { vao: handle.vao, vbo, ibo };\n this.persistent.add(resources);\n // Register so the GL resources get freed when `mesh` is GC'd. The\n // unregister token (mesh) lets us cancel if the Mesh is somehow\n // re-uploaded under a different cache, though that's not a normal flow.\n this.finalizer.register(mesh, resources, mesh);\n return handle;\n }\n\n /**\n * Upload a Mesh that the caller knows is single-use (e.g. the per-frame\n * stroke ribbon from `tessellateStroke`). Returns a handle backed by fresh\n * GL resources that the renderer will free deterministically at the end\n * of the current frame via `freeTransient()`. Bypasses the WeakMap cache\n * and the FinalizationRegistry, so transient meshes never wait on GC.\n */\n uploadTransient(mesh: Mesh): GLMeshHandle {\n const { handle, vbo, ibo } = this.upload(mesh);\n this.transientThisFrame.push({ vao: handle.vao, vbo, ibo });\n return handle;\n }\n\n /**\n * Upload a Mesh that may or may not recur across frames. Its first sight in\n * *this* context takes `uploadTransient`; every later one takes the\n * persistent handle. One transient upload to find out is cheaper than\n * stranding a persistent VAO on a mesh that never returns, whose release\n * would wait on GC. The transient upload does not populate the persistent\n * map, so steady-state reuse begins on the third frame.\n */\n uploadRecurring(mesh: Mesh): GLMeshHandle {\n if (this.seen.has(mesh)) return this.handleFor(mesh);\n this.seen.add(mesh);\n return this.uploadTransient(mesh);\n }\n\n /**\n * Free all transient resources allocated since the last call. Called by\n * the renderer at the end of each `render()`. Safe under context loss.\n */\n freeTransient(): void {\n if (this.transientThisFrame.length === 0) return;\n const gl = this.gl;\n if (gl.isContextLost()) {\n this.transientThisFrame.length = 0;\n return;\n }\n for (const r of this.transientThisFrame) {\n gl.deleteVertexArray(r.vao);\n gl.deleteBuffer(r.vbo);\n gl.deleteBuffer(r.ibo);\n }\n this.transientThisFrame.length = 0;\n }\n\n /** @internal — for tests asserting the transient-list size. */\n _transientCount(): number {\n return this.transientThisFrame.length;\n }\n\n /**\n * Free GL resources whose Mesh has been GC'd since the last drain.\n * Called by the renderer at the top of each `render()`, before any draws.\n */\n drainPendingDeletes(): void {\n if (this.pendingDeletes.length === 0) return;\n const gl = this.gl;\n if (gl.isContextLost()) {\n this.pendingDeletes.length = 0;\n return;\n }\n for (const r of this.pendingDeletes) {\n gl.deleteVertexArray(r.vao);\n gl.deleteBuffer(r.vbo);\n gl.deleteBuffer(r.ibo);\n }\n this.pendingDeletes.length = 0;\n }\n\n /**\n * Free every GL resource this cache holds — persistent, pending and\n * transient — and ignore finalizers from here on. Idempotent. Unlike the\n * other paths this does not wait on GC, which is what lets a renderer on a\n * context it does not own give back everything it uploaded.\n */\n dispose(): void {\n if (this.disposed) return;\n this.disposed = true;\n this.freeTransient();\n for (const r of this.persistent) this.pendingDeletes.push(r);\n this.persistent.clear();\n this.drainPendingDeletes();\n }\n\n /** @internal — for tests asserting the queue size. */\n _pendingDeleteCount(): number {\n return this.pendingDeletes.length;\n }\n\n /** @internal — for tests that need to simulate the finalizer firing. */\n _enqueueDeleteForTest(resources: MeshResources): void {\n this.pendingDeletes.push(resources);\n }\n\n private upload(mesh: Mesh): { handle: GLMeshHandle; vbo: WebGLBuffer; ibo: WebGLBuffer } {\n const gl = this.gl;\n const vao = gl.createVertexArray();\n if (!vao) throw new Error('createVertexArray returned null');\n gl.bindVertexArray(vao);\n\n const vbo = gl.createBuffer();\n if (!vbo) throw new Error('createBuffer (VBO) returned null');\n gl.bindBuffer(gl.ARRAY_BUFFER, vbo);\n gl.bufferData(gl.ARRAY_BUFFER, mesh.vertices, gl.STATIC_DRAW);\n\n gl.enableVertexAttribArray(this.aPositionLoc);\n gl.vertexAttribPointer(this.aPositionLoc, 2, gl.FLOAT, false, 0, 0);\n\n const ibo = gl.createBuffer();\n if (!ibo) throw new Error('createBuffer (IBO) returned null');\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);\n gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, mesh.indices, gl.STATIC_DRAW);\n\n gl.bindVertexArray(null);\n\n return {\n handle: {\n vao,\n indexCount: mesh.indices.length,\n requiresStencil: mesh.requiresStencil ?? false,\n anchorA: mesh.anchorA,\n anchorB: mesh.anchorB,\n anchorT: mesh.anchorT,\n },\n vbo,\n ibo,\n };\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"GLMeshCache.js","names":["GLMeshCache","gl","resources","cached","mesh","handle","r","vao","vbo","POSITION_ATTRIBUTE_LOCATION","ibo"],"sources":["../../../src/renderer/cache/GLMeshCache.ts"],"sourcesContent":["import type { Mesh } from '@weasel-js/geom/tessellate';\nimport { POSITION_ATTRIBUTE_LOCATION } from '../shaders/ShaderProgram';\n\nexport interface GLMeshHandle {\n readonly vao: WebGLVertexArrayObject;\n readonly indexCount: number;\n readonly requiresStencil: boolean;\n readonly anchorA?: Uint32Array;\n readonly anchorB?: Uint32Array;\n readonly anchorT?: Float32Array;\n}\n\n/** Resources to release when a Mesh is reclaimed by GC. */\ninterface MeshResources {\n vao: WebGLVertexArrayObject;\n vbo: WebGLBuffer;\n ibo: WebGLBuffer;\n}\n\n/**\n * Caches GL-side buffers + VAO per `Mesh` identity. Upload happens lazily\n * on first `handleFor(mesh)` call.\n *\n * **GC-aware cleanup (deferred-delete queue):** when a Mesh becomes\n * unreachable, the WeakMap entry is dropped automatically — but the\n * underlying GL resources (VAO, VBO, IBO) would leak forever without an\n * explicit `gl.delete*` call. We register each Mesh with a\n * `FinalizationRegistry`; when the Mesh is reclaimed, the finalizer pushes\n * the resources onto an internal queue. The renderer drains this queue at\n * the start of each `render()` call (via `drainPendingDeletes()`), when GL\n * is in a known state — no VAO bound, no draw mid-flight. Deleting from the\n * finalizer directly was racy (it could fire between two `gl.bindVertexArray`\n * calls inside `dispatch()`, sometimes corrupting the next draw); deferring\n * the actual delete to a known-safe point keeps the use-after-free risk to\n * zero.\n *\n * Caveats:\n * - Finalizer timing is non-deterministic; resources may live for a frame\n * or two beyond their Mesh, but they will be reclaimed.\n * - If the GL context is lost between finalizer and drain, the drain\n * no-ops safely (`gl.isContextLost()` makes deletes no-ops, and\n * `gl.deleteBuffer(null)` is allowed).\n *\n * The cache is GL-context-bound; if the context is lost and re-created, the\n * renderer should construct a new GLMeshCache. (Context loss handling lives\n * in `WeaselRenderer`.)\n */\nexport class GLMeshCache {\n private readonly map = new WeakMap<Mesh, GLMeshHandle>();\n /** Meshes this context has drawn at least once; see `uploadRecurring`. */\n private readonly seen = new WeakSet<Mesh>();\n private readonly finalizer: FinalizationRegistry<MeshResources>;\n /** Every persistent upload not yet freed, so `dispose` can reach them. */\n private readonly persistent = new Set<MeshResources>();\n private readonly pendingDeletes: MeshResources[] = [];\n private disposed = false;\n /** Transient resources allocated this frame; freed at end of render(). */\n private readonly transientThisFrame: MeshResources[] = [];\n\n constructor(\n private readonly gl: WebGL2RenderingContext,\n ) {\n this.finalizer = new FinalizationRegistry<MeshResources>((resources) => {\n // Defer the actual gl.delete* until the renderer's next render(),\n // when no VAO/buffer is bound mid-draw. Direct deletion from the\n // finalizer caused use-after-free crashes when GC fired between\n // bindVertexArray and drawElements inside dispatch().\n if (!this.persistent.delete(resources)) return;\n this.pendingDeletes.push(resources);\n });\n }\n\n handleFor(mesh: Mesh): GLMeshHandle {\n const cached = this.map.get(mesh);\n if (cached) return cached;\n const { handle, vbo, ibo } = this.upload(mesh);\n this.map.set(mesh, handle);\n const resources = { vao: handle.vao, vbo, ibo };\n this.persistent.add(resources);\n // Register so the GL resources get freed when `mesh` is GC'd. The\n // unregister token (mesh) lets us cancel if the Mesh is somehow\n // re-uploaded under a different cache, though that's not a normal flow.\n this.finalizer.register(mesh, resources, mesh);\n return handle;\n }\n\n /**\n * Upload a Mesh that the caller knows is single-use (e.g. the per-frame\n * stroke ribbon from `tessellateStroke`). Returns a handle backed by fresh\n * GL resources that the renderer will free deterministically at the end\n * of the current frame via `freeTransient()`. Bypasses the WeakMap cache\n * and the FinalizationRegistry, so transient meshes never wait on GC.\n */\n uploadTransient(mesh: Mesh): GLMeshHandle {\n const { handle, vbo, ibo } = this.upload(mesh);\n this.transientThisFrame.push({ vao: handle.vao, vbo, ibo });\n return handle;\n }\n\n /**\n * Upload a Mesh that may or may not recur across frames. Its first sight in\n * *this* context takes `uploadTransient`; every later one takes the\n * persistent handle. One transient upload to find out is cheaper than\n * stranding a persistent VAO on a mesh that never returns, whose release\n * would wait on GC. The transient upload does not populate the persistent\n * map, so steady-state reuse begins on the third frame.\n */\n uploadRecurring(mesh: Mesh): GLMeshHandle {\n if (this.seen.has(mesh)) return this.handleFor(mesh);\n this.seen.add(mesh);\n return this.uploadTransient(mesh);\n }\n\n /**\n * Free all transient resources allocated since the last call. Called by\n * the renderer at the end of each `render()`. Safe under context loss.\n */\n freeTransient(): void {\n if (this.transientThisFrame.length === 0) return;\n const gl = this.gl;\n if (gl.isContextLost()) {\n this.transientThisFrame.length = 0;\n return;\n }\n for (const r of this.transientThisFrame) {\n gl.deleteVertexArray(r.vao);\n gl.deleteBuffer(r.vbo);\n gl.deleteBuffer(r.ibo);\n }\n this.transientThisFrame.length = 0;\n }\n\n /** @internal — for tests asserting the transient-list size. */\n _transientCount(): number {\n return this.transientThisFrame.length;\n }\n\n /**\n * Free GL resources whose Mesh has been GC'd since the last drain.\n * Called by the renderer at the top of each `render()`, before any draws.\n */\n drainPendingDeletes(): void {\n if (this.pendingDeletes.length === 0) return;\n const gl = this.gl;\n if (gl.isContextLost()) {\n this.pendingDeletes.length = 0;\n return;\n }\n for (const r of this.pendingDeletes) {\n gl.deleteVertexArray(r.vao);\n gl.deleteBuffer(r.vbo);\n gl.deleteBuffer(r.ibo);\n }\n this.pendingDeletes.length = 0;\n }\n\n /**\n * Free every GL resource this cache holds — persistent, pending and\n * transient — and ignore finalizers from here on. Idempotent. Unlike the\n * other paths this does not wait on GC, which is what lets a renderer on a\n * context it does not own give back everything it uploaded.\n */\n dispose(): void {\n if (this.disposed) return;\n this.disposed = true;\n this.freeTransient();\n for (const r of this.persistent) this.pendingDeletes.push(r);\n this.persistent.clear();\n this.drainPendingDeletes();\n }\n\n /** @internal — for tests asserting the queue size. */\n _pendingDeleteCount(): number {\n return this.pendingDeletes.length;\n }\n\n /** @internal — for tests that need to simulate the finalizer firing. */\n _enqueueDeleteForTest(resources: MeshResources): void {\n this.pendingDeletes.push(resources);\n }\n\n private upload(mesh: Mesh): { handle: GLMeshHandle; vbo: WebGLBuffer; ibo: WebGLBuffer } {\n const gl = this.gl;\n const vao = gl.createVertexArray();\n if (!vao) throw new Error('createVertexArray returned null');\n gl.bindVertexArray(vao);\n\n const vbo = gl.createBuffer();\n if (!vbo) throw new Error('createBuffer (VBO) returned null');\n gl.bindBuffer(gl.ARRAY_BUFFER, vbo);\n gl.bufferData(gl.ARRAY_BUFFER, mesh.vertices, gl.STATIC_DRAW);\n\n gl.enableVertexAttribArray(POSITION_ATTRIBUTE_LOCATION);\n gl.vertexAttribPointer(POSITION_ATTRIBUTE_LOCATION, 2, gl.FLOAT, false, 0, 0);\n\n const ibo = gl.createBuffer();\n if (!ibo) throw new Error('createBuffer (IBO) returned null');\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);\n gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, mesh.indices, gl.STATIC_DRAW);\n\n gl.bindVertexArray(null);\n\n return {\n handle: {\n vao,\n indexCount: mesh.indices.length,\n requiresStencil: mesh.requiresStencil ?? false,\n anchorA: mesh.anchorA,\n anchorB: mesh.anchorB,\n anchorT: mesh.anchorT,\n },\n vbo,\n ibo,\n };\n }\n}\n"],"mappings":"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"}
|
|
@@ -3,6 +3,8 @@ var e = class {
|
|
|
3
3
|
gl;
|
|
4
4
|
map = /* @__PURE__ */ new Map();
|
|
5
5
|
wraps = /* @__PURE__ */ new Map();
|
|
6
|
+
registered = /* @__PURE__ */ new Map();
|
|
7
|
+
sweptVersion = -1;
|
|
6
8
|
constructor(e) {
|
|
7
9
|
this.gl = e;
|
|
8
10
|
}
|
|
@@ -17,6 +19,21 @@ var e = class {
|
|
|
17
19
|
let o = n === "repeat" ? i.REPEAT : i.CLAMP_TO_EDGE;
|
|
18
20
|
return i.bindTexture(i.TEXTURE_2D, a), i.texImage2D(i.TEXTURE_2D, 0, i.RGBA, i.RGBA, i.UNSIGNED_BYTE, t), i.texParameteri(i.TEXTURE_2D, i.TEXTURE_MIN_FILTER, i.LINEAR), i.texParameteri(i.TEXTURE_2D, i.TEXTURE_MAG_FILTER, i.LINEAR), i.texParameteri(i.TEXTURE_2D, i.TEXTURE_WRAP_S, o), i.texParameteri(i.TEXTURE_2D, i.TEXTURE_WRAP_T, o), i.bindTexture(i.TEXTURE_2D, null), this.map.set(e, a), this.wraps.set(e, n), e;
|
|
19
21
|
}
|
|
22
|
+
uploadRegistered(e, t, n = "clamp") {
|
|
23
|
+
let r = this.map.get(e);
|
|
24
|
+
if (r && this.registered.get(e) !== t) {
|
|
25
|
+
let e = this.gl;
|
|
26
|
+
e.bindTexture(e.TEXTURE_2D, r), e.texImage2D(e.TEXTURE_2D, 0, e.RGBA, e.RGBA, e.UNSIGNED_BYTE, t), e.bindTexture(e.TEXTURE_2D, null);
|
|
27
|
+
}
|
|
28
|
+
return this.registered.set(e, t), this.upload(e, t, n);
|
|
29
|
+
}
|
|
30
|
+
sweep(e) {
|
|
31
|
+
let t = e.getVersion();
|
|
32
|
+
if (t !== this.sweptVersion) {
|
|
33
|
+
this.sweptVersion = t;
|
|
34
|
+
for (let t of this.registered.keys()) e.has(t) || (this.gl.deleteTexture(this.map.get(t) ?? null), this.map.delete(t), this.wraps.delete(t), this.registered.delete(t));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
20
37
|
applyWrap(e, t, n) {
|
|
21
38
|
let r = this.gl, i = n === "repeat" ? r.REPEAT : r.CLAMP_TO_EDGE;
|
|
22
39
|
r.bindTexture(r.TEXTURE_2D, t), r.texParameteri(r.TEXTURE_2D, r.TEXTURE_WRAP_S, i), r.texParameteri(r.TEXTURE_2D, r.TEXTURE_WRAP_T, i), r.bindTexture(r.TEXTURE_2D, null), this.wraps.set(e, n);
|
|
@@ -42,7 +59,7 @@ var e = class {
|
|
|
42
59
|
free() {
|
|
43
60
|
let e = this.gl;
|
|
44
61
|
for (let t of this.map.values()) e.deleteTexture(t);
|
|
45
|
-
this.map.clear(), this.wraps.clear();
|
|
62
|
+
this.map.clear(), this.wraps.clear(), this.registered.clear();
|
|
46
63
|
}
|
|
47
64
|
};
|
|
48
65
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GLTextureCache.js","names":["GLTextureCache","gl","id","cached","wrap","tex","wrapMode","source","mode","width","height","data","x","y","w","h","unit"],"sources":["../../../src/renderer/cache/GLTextureCache.ts"],"sourcesContent":["/**\n * GL texture upload + cache for MSDF font atlases (and future images).\n *\n * Textures are keyed by a string id (the font family name or image id).\n * Pixels are uploaded once per id; a later upload of the same id reuses the\n * texture and only re-applies its wrap mode when that changed.\n * The cache is GL-context-bound; discard and create a new one on context loss.\n *\n * Atlas format: RGBA UNSIGNED_BYTE, linear filtering, no mipmaps.\n * Mipmap generation is deliberately skipped — MSDF works correctly with\n * linear filtering, and mipmap resampling corrupts the multi-channel SDF signal.\n */\n\ntype TexSource = HTMLImageElement | ImageBitmap | ImageData | HTMLCanvasElement;\n\n/** How a texture samples outside `0..1`. Pattern tiles need `'repeat'`;\n * everything else clamps. WebGL2 allows `REPEAT` on NPOT textures, so a\n * tile of any size tiles correctly. */\nexport type TextureWrap = 'clamp' | 'repeat';\n\nexport class GLTextureCache {\n private readonly map = new Map<string, WebGLTexture>();\n private readonly wraps = new Map<string, TextureWrap>();\n\n constructor(private readonly gl: WebGL2RenderingContext) {}\n\n has(id: string): boolean {\n return this.map.has(id);\n }\n\n /** Uploads the pixels once per id. `wrap` is re-applied whenever it differs\n * from what the id was last uploaded under: the same registered image can\n * be a clamped shader texture in one draw and a repeating pattern tile in\n * the next, and first-upload-wins gave the second one the first one's mode. */\n upload(id: string, source: TexSource, wrap: TextureWrap = 'clamp'): string {\n const cached = this.map.get(id);\n if (cached) {\n if (this.wraps.get(id) !== wrap) this.applyWrap(id, cached, wrap);\n return id;\n }\n\n const gl = this.gl;\n const tex = gl.createTexture();\n if (!tex) throw new Error(`GLTextureCache: createTexture failed for id=\"${id}\"`);\n\n const wrapMode = wrap === 'repeat' ? gl.REPEAT : gl.CLAMP_TO_EDGE;\n\n gl.bindTexture(gl.TEXTURE_2D, tex);\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source as TexImageSource);\n\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapMode);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapMode);\n\n gl.bindTexture(gl.TEXTURE_2D, null);\n\n this.map.set(id, tex);\n this.wraps.set(id, wrap);\n return id;\n }\n\n private applyWrap(id: string, tex: WebGLTexture, wrap: TextureWrap): void {\n const gl = this.gl;\n const mode = wrap === 'repeat' ? gl.REPEAT : gl.CLAMP_TO_EDGE;\n gl.bindTexture(gl.TEXTURE_2D, tex);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, mode);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, mode);\n gl.bindTexture(gl.TEXTURE_2D, null);\n this.wraps.set(id, wrap);\n }\n\n /** Create a single-channel R8 texture from raw bytes (full upload).\n * No-op if `id` already exists. Same LINEAR/CLAMP params as `upload`;\n * UNPACK_ALIGNMENT dropped to 1 for non-4-aligned row widths. */\n uploadR8(id: string, width: number, height: number, data: Uint8Array): void {\n if (this.map.has(id)) return;\n const gl = this.gl;\n const tex = gl.createTexture();\n if (!tex) throw new Error(`GLTextureCache: createTexture failed for id=\"${id}\"`);\n gl.bindTexture(gl.TEXTURE_2D, tex);\n gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.R8, width, height, 0, gl.RED, gl.UNSIGNED_BYTE, data);\n gl.pixelStorei(gl.UNPACK_ALIGNMENT, 4);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n gl.bindTexture(gl.TEXTURE_2D, null);\n this.map.set(id, tex);\n this.wraps.set(id, 'clamp');\n }\n\n /** Patch a rect of an existing R8 texture with tightly-packed w×h bytes. */\n subImageR8(id: string, x: number, y: number, w: number, h: number, data: Uint8Array): void {\n const tex = this.map.get(id);\n if (!tex) throw new Error(`GLTextureCache: texture \"${id}\" not uploaded`);\n const gl = this.gl;\n gl.bindTexture(gl.TEXTURE_2D, tex);\n gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);\n gl.texSubImage2D(gl.TEXTURE_2D, 0, x, y, w, h, gl.RED, gl.UNSIGNED_BYTE, data);\n gl.pixelStorei(gl.UNPACK_ALIGNMENT, 4);\n gl.bindTexture(gl.TEXTURE_2D, null);\n }\n\n bind(id: string, unit: number): void {\n const tex = this.map.get(id);\n if (!tex) throw new Error(`GLTextureCache: texture \"${id}\" not uploaded`);\n const gl = this.gl;\n gl.activeTexture(gl.TEXTURE0 + unit);\n gl.bindTexture(gl.TEXTURE_2D, tex);\n }\n\n /**\n * Delete every uploaded GL texture and clear the map. Called by\n * `WeaselRenderer.dispose()`. The cache is unusable but refillable\n * afterward — a subsequent `upload()` for a previously-seen id re-creates\n * the texture rather than restoring the deleted one.\n */\n free(): void {\n const gl = this.gl;\n for (const tex of this.map.values()) {\n gl.deleteTexture(tex);\n }\n this.map.clear();\n this.wraps.clear();\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"GLTextureCache.js","names":["GLTextureCache","gl","id","cached","wrap","tex","wrapMode","source","version","registry","mode","width","height","data","x","y","w","h","unit"],"sources":["../../../src/renderer/cache/GLTextureCache.ts"],"sourcesContent":["/**\n * GL texture upload + cache for MSDF font atlases (and future images).\n *\n * Textures are keyed by a string id (the font family name or image id).\n * Pixels are uploaded once per id; a later upload of the same id reuses the\n * texture and only re-applies its wrap mode when that changed.\n * The cache is GL-context-bound; discard and create a new one on context loss.\n *\n * Atlas format: RGBA UNSIGNED_BYTE, linear filtering, no mipmaps.\n * Mipmap generation is deliberately skipped — MSDF works correctly with\n * linear filtering, and mipmap resampling corrupts the multi-channel SDF signal.\n */\n\ntype TexSource = HTMLImageElement | ImageBitmap | ImageData | HTMLCanvasElement;\n\n/** How a texture samples outside `0..1`. Pattern tiles need `'repeat'`;\n * everything else clamps. WebGL2 allows `REPEAT` on NPOT textures, so a\n * tile of any size tiles correctly. */\nexport type TextureWrap = 'clamp' | 'repeat';\n\n/** The part of the texture registry a sweep reads. */\nexport interface RegisteredTextureIds {\n getVersion(): number;\n has(id: string): boolean;\n}\n\nexport class GLTextureCache {\n private readonly map = new Map<string, WebGLTexture>();\n private readonly wraps = new Map<string, TextureWrap>();\n /** The source each registry-backed id was last uploaded from. */\n private readonly registered = new Map<string, TexSource>();\n private sweptVersion = -1;\n\n constructor(private readonly gl: WebGL2RenderingContext) {}\n\n has(id: string): boolean {\n return this.map.has(id);\n }\n\n /** Uploads the pixels once per id. `wrap` is re-applied whenever it differs\n * from what the id was last uploaded under: the same registered image can\n * be a clamped shader texture in one draw and a repeating pattern tile in\n * the next, and first-upload-wins gave the second one the first one's mode. */\n upload(id: string, source: TexSource, wrap: TextureWrap = 'clamp'): string {\n const cached = this.map.get(id);\n if (cached) {\n if (this.wraps.get(id) !== wrap) this.applyWrap(id, cached, wrap);\n return id;\n }\n\n const gl = this.gl;\n const tex = gl.createTexture();\n if (!tex) throw new Error(`GLTextureCache: createTexture failed for id=\"${id}\"`);\n\n const wrapMode = wrap === 'repeat' ? gl.REPEAT : gl.CLAMP_TO_EDGE;\n\n gl.bindTexture(gl.TEXTURE_2D, tex);\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source as TexImageSource);\n\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapMode);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapMode);\n\n gl.bindTexture(gl.TEXTURE_2D, null);\n\n this.map.set(id, tex);\n this.wraps.set(id, wrap);\n return id;\n }\n\n /** `upload` for an id from the texture registry, whose image can be replaced\n * under the same id: a changed source sends its pixels again. */\n uploadRegistered(id: string, source: TexSource, wrap: TextureWrap = 'clamp'): string {\n const cached = this.map.get(id);\n if (cached && this.registered.get(id) !== source) {\n const gl = this.gl;\n gl.bindTexture(gl.TEXTURE_2D, cached);\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source as TexImageSource);\n gl.bindTexture(gl.TEXTURE_2D, null);\n }\n this.registered.set(id, source);\n return this.upload(id, source, wrap);\n }\n\n /**\n * Delete the texture of every registry-backed id the registry has dropped.\n * The renderer calls this at the top of `render()`, before any draw, so a\n * texture is never deleted under a draw that has it bound.\n */\n sweep(registry: RegisteredTextureIds): void {\n const version = registry.getVersion();\n if (version === this.sweptVersion) return;\n this.sweptVersion = version;\n for (const id of this.registered.keys()) {\n if (registry.has(id)) continue;\n this.gl.deleteTexture(this.map.get(id) ?? null);\n this.map.delete(id);\n this.wraps.delete(id);\n this.registered.delete(id);\n }\n }\n\n private applyWrap(id: string, tex: WebGLTexture, wrap: TextureWrap): void {\n const gl = this.gl;\n const mode = wrap === 'repeat' ? gl.REPEAT : gl.CLAMP_TO_EDGE;\n gl.bindTexture(gl.TEXTURE_2D, tex);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, mode);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, mode);\n gl.bindTexture(gl.TEXTURE_2D, null);\n this.wraps.set(id, wrap);\n }\n\n /** Create a single-channel R8 texture from raw bytes (full upload).\n * No-op if `id` already exists. Same LINEAR/CLAMP params as `upload`;\n * UNPACK_ALIGNMENT dropped to 1 for non-4-aligned row widths. */\n uploadR8(id: string, width: number, height: number, data: Uint8Array): void {\n if (this.map.has(id)) return;\n const gl = this.gl;\n const tex = gl.createTexture();\n if (!tex) throw new Error(`GLTextureCache: createTexture failed for id=\"${id}\"`);\n gl.bindTexture(gl.TEXTURE_2D, tex);\n gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.R8, width, height, 0, gl.RED, gl.UNSIGNED_BYTE, data);\n gl.pixelStorei(gl.UNPACK_ALIGNMENT, 4);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n gl.bindTexture(gl.TEXTURE_2D, null);\n this.map.set(id, tex);\n this.wraps.set(id, 'clamp');\n }\n\n /** Patch a rect of an existing R8 texture with tightly-packed w×h bytes. */\n subImageR8(id: string, x: number, y: number, w: number, h: number, data: Uint8Array): void {\n const tex = this.map.get(id);\n if (!tex) throw new Error(`GLTextureCache: texture \"${id}\" not uploaded`);\n const gl = this.gl;\n gl.bindTexture(gl.TEXTURE_2D, tex);\n gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);\n gl.texSubImage2D(gl.TEXTURE_2D, 0, x, y, w, h, gl.RED, gl.UNSIGNED_BYTE, data);\n gl.pixelStorei(gl.UNPACK_ALIGNMENT, 4);\n gl.bindTexture(gl.TEXTURE_2D, null);\n }\n\n bind(id: string, unit: number): void {\n const tex = this.map.get(id);\n if (!tex) throw new Error(`GLTextureCache: texture \"${id}\" not uploaded`);\n const gl = this.gl;\n gl.activeTexture(gl.TEXTURE0 + unit);\n gl.bindTexture(gl.TEXTURE_2D, tex);\n }\n\n /**\n * Delete every uploaded GL texture and clear the map. Called by\n * `WeaselRenderer.dispose()`. The cache is unusable but refillable\n * afterward — a subsequent `upload()` for a previously-seen id re-creates\n * the texture rather than restoring the deleted one.\n */\n free(): void {\n const gl = this.gl;\n for (const tex of this.map.values()) {\n gl.deleteTexture(tex);\n }\n this.map.clear();\n this.wraps.clear();\n this.registered.clear();\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.js","names":["cache","getMesh","cached","path","mesh","tessellate"],"sources":["../../../src/renderer/cache/cache.ts"],"sourcesContent":["import type { Path } from '@weasel-js/
|
|
1
|
+
{"version":3,"file":"cache.js","names":["cache","getMesh","cached","path","mesh","tessellate"],"sources":["../../../src/renderer/cache/cache.ts"],"sourcesContent":["import type { Path } from '@weasel-js/geom';\nimport { type Mesh, tessellate } from '@weasel-js/geom/tessellate';\n\nlet cache = new WeakMap<Path, Mesh>();\n\n/**\n * Return the tessellated Mesh for `path`, computing and caching on first call.\n * Cached by Path identity (WeakMap) — different Path objects with the same\n * coords are distinct cache entries.\n *\n * A rect path the batch stages as a quad never reaches this cache. One used\n * as a clip, or filled with a pattern or vertex colors, does.\n *\n * Also bypassed whenever `DrawContext.flattenTolerance` is set: `fillMeshHandle`\n * (see `../draw`) routes custom-tolerance fills through a fresh transient\n * tessellation instead of this cache.\n */\nexport function getMesh(path: Path): Mesh {\n const cached = cache.get(path);\n if (cached !== undefined) return cached;\n const mesh = tessellate(path);\n cache.set(path, mesh);\n return mesh;\n}\n\n/** Test helper. Do not call from product code. */\nexport function _resetCacheForTests(): void {\n cache = new WeakMap();\n}\n"],"mappings":";;AAGA,IAAIA,oBAAQ,IAAI,QAAoB;AAcpC,SAAgBC,EAAQ,GAAkB;CACxC,IAAMC,IAASF,EAAM,IAAIG,CAAI;CAC7B,IAAID,MAAW,KAAA,GAAW,OAAOA;CACjC,IAAME,IAAOC,EAAWF,CAAI;CAE5B,OADAH,EAAM,IAAIG,GAAMC,CAAI,GACbA;AACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markerCommandCache.js","names":["byValue","byStroke","refKey","ref","readsKey","key","stroke","entry","getMarker","markerKeyOf","reads","markerReadsKey","mapFor","map","path","strokes","source","cachedMarkerCommands","strokeWidth","flattenTolerance","markerGeneration","screenSpaceKey","spaces","heads","markerHeads","headCommands","STROKE_CONFIGS_PER_PATH"],"sources":["../../../src/renderer/cache/markerCommandCache.ts"],"sourcesContent":["/**\n * A stroke's marker commands, cached per path. The placed head is a fresh\n * `Path`, and the fill cache keys on path identity — so rebuilding it every\n * frame would re-tessellate and re-upload every head every frame.\n *\n * Keyed on value, so a painter that builds a new stroke object every frame\n * still hits: the resolved lengths, the flatten tolerance, the registry's\n * generation, the marker refs and whatever fields each head declares it reads.\n * A head that does not declare them may read any field, so for it the stroke\n * as the command carried it — before any `{ px }` length was resolved — joins\n * the key by identity. Paint is applied over the cached geometry, so a new\n * paint reuses the heads.\n */\n\nimport type { Path } from '@weasel-js/
|
|
1
|
+
{"version":3,"file":"markerCommandCache.js","names":["byValue","byStroke","refKey","ref","readsKey","key","stroke","entry","getMarker","markerKeyOf","reads","markerReadsKey","mapFor","map","path","strokes","source","cachedMarkerCommands","strokeWidth","flattenTolerance","markerGeneration","screenSpaceKey","spaces","heads","markerHeads","headCommands","STROKE_CONFIGS_PER_PATH"],"sources":["../../../src/renderer/cache/markerCommandCache.ts"],"sourcesContent":["/**\n * A stroke's marker commands, cached per path. The placed head is a fresh\n * `Path`, and the fill cache keys on path identity — so rebuilding it every\n * frame would re-tessellate and re-upload every head every frame.\n *\n * Keyed on value, so a painter that builds a new stroke object every frame\n * still hits: the resolved lengths, the flatten tolerance, the registry's\n * generation, the marker refs and whatever fields each head declares it reads.\n * A head that does not declare them may read any field, so for it the stroke\n * as the command carried it — before any `{ px }` length was resolved — joins\n * the key by identity. Paint is applied over the cached geometry, so a new\n * paint reuses the heads.\n */\n\nimport type { Path } from '@weasel-js/geom';\nimport type { CommandFill, MarkerRef, CommandStroke } from '@weasel-js/paint';\nimport type { PathDrawCommand } from '../DrawCommand';\nimport {\n headCommands, markerHeads, markerReadsKey, type MarkerHead,\n} from 'features/paths/markerCommands';\nimport { getMarker, markerGeneration } from '../../core/strokeMarkers';\nimport { markerKeyOf } from '../../core/markerInset';\nimport { STROKE_CONFIGS_PER_PATH } from './strokeMeshCache';\nimport { screenSpaceKey, type StrokeSpaces } from 'features/paths/tessellate/metric';\n\ninterface Entry {\n readonly heads: MarkerHead[];\n paint: CommandFill | undefined;\n cmds: PathDrawCommand[];\n}\n\nlet byValue = new WeakMap<Path, Map<string, Entry>>();\nlet byStroke = new WeakMap<Path, WeakMap<CommandStroke, Map<string, Entry>>>();\n\nconst refKey = (ref: MarkerRef | undefined): string =>\n ref === undefined ? '' : typeof ref === 'string' ? ref : `${ref.key}@${String(ref.size ?? '')}`;\n\n/** What every head of `stroke` reads, or `null` if any head does not say. */\nfunction readsKey(stroke: CommandStroke): string | null {\n let key = '';\n for (const ref of [stroke.markerStart, stroke.markerMid, stroke.markerEnd]) {\n const entry = ref === undefined ? undefined : getMarker(markerKeyOf(ref));\n if (entry === undefined) {\n key += '/';\n continue;\n }\n const reads = markerReadsKey(entry, stroke);\n if (reads === null) return null;\n key += `${reads}/`;\n }\n return key;\n}\n\nfunction mapFor(path: Path, source: CommandStroke, reads: string | null): Map<string, Entry> {\n if (reads !== null) {\n let map = byValue.get(path);\n if (map === undefined) byValue.set(path, (map = new Map()));\n return map;\n }\n let strokes = byStroke.get(path);\n if (strokes === undefined) byStroke.set(path, (strokes = new WeakMap()));\n let map = strokes.get(source);\n if (map === undefined) strokes.set(source, (map = new Map()));\n return map;\n}\n\n/**\n * The marker commands for `path` under `stroke`, whose lengths are already\n * resolved. `source` is the stroke before that resolution. A head's lengths are\n * in the space `spaces` builds it in.\n */\nexport function cachedMarkerCommands(\n path: Path,\n source: CommandStroke,\n stroke: CommandStroke,\n strokeWidth: number,\n flattenTolerance: number | undefined,\n spaces: StrokeSpaces = {},\n): PathDrawCommand[] {\n if (stroke.markerStart === undefined && stroke.markerMid === undefined && stroke.markerEnd === undefined) {\n return [];\n }\n const reads = readsKey(stroke);\n const map = mapFor(path, source, reads);\n const key = [\n strokeWidth,\n flattenTolerance ?? '',\n markerGeneration(),\n refKey(stroke.markerStart),\n refKey(stroke.markerMid),\n refKey(stroke.markerEnd),\n reads ?? '',\n screenSpaceKey(spaces.start),\n screenSpaceKey(spaces.mid),\n screenSpaceKey(spaces.end),\n ].join('|');\n let entry = map.get(key);\n if (entry === undefined) {\n const heads = markerHeads(path, stroke, strokeWidth, flattenTolerance, spaces);\n entry = { heads, paint: stroke.paint, cmds: headCommands(heads, stroke) };\n if (map.size >= STROKE_CONFIGS_PER_PATH) map.clear();\n map.set(key, entry);\n } else if (entry.paint !== stroke.paint) {\n entry.paint = stroke.paint;\n entry.cmds = headCommands(entry.heads, stroke);\n }\n return entry.cmds;\n}\n\n/** Test helper. Do not call from product code. */\nexport function _resetMarkerCommandCacheForTests(): void {\n byValue = new WeakMap();\n byStroke = new WeakMap();\n}\n"],"mappings":";;;;;;AA+BA,IAAIA,oBAAU,IAAI,QAAkC,GAChDC,oBAAW,IAAI,QAA0D,GAEvEC,KAAU,MACdC,MAAQ,KAAA,IAAY,KAAK,OAAOA,KAAQ,WAAWA,IAAM,GAAGA,EAAI,IAAI,GAAG,OAAOA,EAAI,QAAQ,EAAE;AAG9F,SAASC,EAAS,GAAsC;CACtD,IAAIC,IAAM;CACV,KAAK,IAAMF,KAAO;EAACG,EAAO;EAAaA,EAAO;EAAWA,EAAO;CAAS,GAAG;EAC1E,IAAMC,IAAQJ,MAAQ,KAAA,IAAY,KAAA,IAAYK,EAAUC,EAAYN,CAAG,CAAC;EACxE,IAAII,MAAU,KAAA,GAAW;GACvB,KAAO;GACP;EACF;EACA,IAAMG,IAAQC,EAAeJ,GAAOD,CAAM;EAC1C,IAAII,MAAU,MAAM,OAAO;EAC3B,KAAO,GAAGA,EAAM;CAClB;CACA,OAAOL;AACT;AAEA,SAASO,EAAO,GAAY,GAAuB,GAA0C;CAC3F,IAAIF,MAAU,MAAM;EAClB,IAAIG,IAAMb,EAAQ,IAAIc,CAAI;EAE1B,OADID,MAAQ,KAAA,KAAWb,EAAQ,IAAIc,GAAO,oBAAM,IAAI,IAAI,CAAE,GACnDD;CACT;CACA,IAAIE,IAAUd,EAAS,IAAIa,CAAI;CAC/B,AAAIC,MAAY,KAAA,KAAWd,EAAS,IAAIa,GAAO,oBAAU,IAAI,QAAQ,CAAE;CACvE,IAAID,IAAME,EAAQ,IAAIC,CAAM;CAE5B,OADIH,MAAQ,KAAA,KAAWE,EAAQ,IAAIC,GAAS,oBAAM,IAAI,IAAI,CAAE,GACrDH;AACT;AAOA,SAAgBI,EACd,GACA,GACA,GACA,GACA,GACA,IAAuB,CAAC,GACL;CACnB,IAAIX,EAAO,gBAAgB,KAAA,KAAaA,EAAO,cAAc,KAAA,KAAaA,EAAO,cAAc,KAAA,GAC7F,OAAO,CAAC;CAEV,IAAMI,IAAQN,EAASE,CAAM,GACvBO,IAAMD,EAAOE,GAAME,GAAQN,CAAK,GAChCL,IAAM;EACVa;EACAC,KAAoB;EACpBC,EAAiB;EACjBlB,EAAOI,EAAO,WAAW;EACzBJ,EAAOI,EAAO,SAAS;EACvBJ,EAAOI,EAAO,SAAS;EACvBI,KAAS;EACTW,EAAeC,EAAO,KAAK;EAC3BD,EAAeC,EAAO,GAAG;EACzBD,EAAeC,EAAO,GAAG;CAC3B,CAAC,CAAC,KAAK,GAAG,GACNf,IAAQM,EAAI,IAAIR,CAAG;CACvB,IAAIE,MAAU,KAAA,GAAW;EACvB,IAAMgB,IAAQC,EAAYV,GAAMR,GAAQY,GAAaC,GAAkBG,CAAM;EAG7E,AAFA,IAAQ;GAAE;GAAO,OAAOhB,EAAO;GAAO,MAAMmB,EAAaF,GAAOjB,CAAM;EAAE,GACpEO,EAAI,QAAQa,KAAyBb,EAAI,MAAM,GACnDA,EAAI,IAAIR,GAAKE,CAAK;CACpB,OAAO,AAAIA,EAAM,UAAUD,EAAO,UAChC,EAAM,QAAQA,EAAO,OACrB,EAAM,OAAOmB,EAAalB,EAAM,OAAOD,CAAM;CAE/C,OAAOC,EAAM;AACf"}
|