@jbrowse/render-core 4.3.0 → 5.0.0-beta.10
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/esm/RenderCanvas.d.ts +2 -2
- package/esm/RenderLifecycleMixin.d.ts +52 -20
- package/esm/RenderLifecycleMixin.js +89 -23
- package/esm/canvas2dUtils.d.ts +90 -23
- package/esm/canvas2dUtils.js +119 -36
- package/esm/colorRampLut.d.ts +5 -5
- package/esm/colorRampLut.js +6 -6
- package/esm/computePipeline.d.ts +14 -0
- package/esm/computePipeline.js +62 -0
- package/esm/contractReports.d.ts +54 -0
- package/esm/contractReports.js +112 -0
- package/esm/coverageBand.d.ts +13 -8
- package/esm/coverageBand.js +15 -5
- package/esm/coverageBandBuffers.d.ts +2 -9
- package/esm/coverageBandBuffers.js +1 -13
- package/esm/createRenderingBackend.d.ts +14 -18
- package/esm/createRenderingBackend.js +4 -2
- package/esm/displayPhase.d.ts +83 -104
- package/esm/displayPhase.js +42 -82
- package/esm/encodeMemo.d.ts +14 -0
- package/esm/encodeMemo.js +48 -0
- package/esm/gpuContextLostError.d.ts +30 -0
- package/esm/gpuContextLostError.js +40 -0
- package/esm/gpuDevice.d.ts +29 -10
- package/esm/gpuDevice.js +35 -16
- package/esm/graphicsCapabilities.d.ts +27 -19
- package/esm/graphicsCapabilities.js +40 -18
- package/esm/hal/createHal.d.ts +4 -5
- package/esm/hal/createHal.js +14 -4
- package/esm/hal/deviceGpuCache.d.ts +25 -30
- package/esm/hal/deviceGpuCache.js +88 -97
- package/esm/hal/gpuHalBase.d.ts +5 -4
- package/esm/hal/gpuHalBase.js +25 -14
- package/esm/hal/index.d.ts +2 -2
- package/esm/hal/index.js +1 -1
- package/esm/hal/mockHal.d.ts +15 -10
- package/esm/hal/mockHal.js +31 -18
- package/esm/hal/oomReporter.d.ts +7 -0
- package/esm/hal/oomReporter.js +10 -0
- package/esm/hal/passIds.d.ts +8 -7
- package/esm/hal/passIds.js +11 -10
- package/esm/hal/regionRegistry.d.ts +0 -1
- package/esm/hal/regionRegistry.js +3 -15
- package/esm/hal/types.d.ts +55 -18
- package/esm/hal/webgl2Hal.d.ts +23 -6
- package/esm/hal/webgl2Hal.js +161 -155
- package/esm/hal/webgpuHal.d.ts +29 -13
- package/esm/hal/webgpuHal.js +221 -153
- package/esm/installUpload.d.ts +16 -3
- package/esm/installUpload.js +13 -41
- package/esm/instanceCache.d.ts +10 -7
- package/esm/instanceCache.js +14 -15
- package/esm/instancePass.d.ts +2 -1
- package/esm/mapUploadSync.d.ts +2 -10
- package/esm/mapUploadSync.js +2 -3
- package/esm/marks/barMark.d.ts +30 -0
- package/esm/marks/barMark.js +149 -0
- package/esm/marks/colorFill.d.ts +17 -0
- package/esm/marks/colorFill.js +26 -0
- package/esm/marks/drawAgainstHit.d.ts +113 -0
- package/esm/marks/drawAgainstHit.js +407 -0
- package/esm/marks/ellipseDistance.d.ts +10 -0
- package/esm/marks/ellipseDistance.js +136 -0
- package/esm/marks/glyphPaint.d.ts +36 -0
- package/esm/marks/glyphPaint.js +77 -0
- package/esm/marks/index.d.ts +33 -0
- package/esm/marks/index.js +20 -0
- package/esm/marks/lineMark.d.ts +37 -0
- package/esm/marks/lineMark.js +416 -0
- package/esm/marks/linkMark.d.ts +76 -0
- package/esm/marks/linkMark.js +585 -0
- package/esm/marks/markBackend.d.ts +118 -0
- package/esm/marks/markBackend.js +196 -0
- package/esm/marks/markHit.d.ts +32 -0
- package/esm/marks/markHit.js +61 -0
- package/esm/marks/markInk.d.ts +18 -0
- package/esm/marks/markInk.js +71 -0
- package/esm/marks/markPaint.d.ts +14 -0
- package/esm/marks/markPaint.js +32 -0
- package/esm/marks/markPlan.d.ts +25 -0
- package/esm/marks/markPlan.js +20 -0
- package/esm/marks/markRamp.d.ts +72 -0
- package/esm/marks/markRamp.js +197 -0
- package/esm/marks/nearestMarkHit.d.ts +55 -0
- package/esm/marks/nearestMarkHit.js +158 -0
- package/esm/marks/pathRecorder.d.ts +14 -0
- package/esm/marks/pathRecorder.js +80 -0
- package/esm/marks/pointMark.d.ts +42 -0
- package/esm/marks/pointMark.js +125 -0
- package/esm/marks/rowLane.d.ts +39 -0
- package/esm/marks/rowLane.js +41 -0
- package/esm/marks/rowSpanIndex.d.ts +29 -0
- package/esm/marks/rowSpanIndex.js +141 -0
- package/esm/marks/rowTable.d.ts +41 -0
- package/esm/marks/rowTable.js +75 -0
- package/esm/marks/ruleMark.d.ts +21 -0
- package/esm/marks/ruleMark.js +91 -0
- package/esm/marks/spanMark.d.ts +42 -0
- package/esm/marks/spanMark.js +81 -0
- package/esm/marks/types.d.ts +261 -0
- package/esm/marks/types.js +175 -0
- package/esm/marks/valueScale.d.ts +23 -0
- package/esm/marks/valueScale.js +11 -0
- package/esm/namedReactions.d.ts +3 -4
- package/esm/namedReactions.js +3 -4
- package/esm/perRegionRenderingBackend.d.ts +21 -3
- package/esm/perRegionRenderingBackend.js +37 -3
- package/esm/regionDataMap.d.ts +2 -2
- package/esm/regionDataMap.js +25 -23
- package/esm/renderBlock.d.ts +14 -0
- package/esm/renderBlock.js +25 -0
- package/esm/renderingBackendBase.d.ts +20 -14
- package/esm/renderingBackendBase.js +12 -7
- package/esm/scoreScale.d.ts +27 -0
- package/esm/scoreScale.js +88 -0
- package/esm/shaders/barMark.generated.d.ts +3 -0
- package/esm/shaders/barMark.generated.js +7 -0
- package/esm/shaders/barMark.glsl.generated.d.ts +2 -0
- package/esm/shaders/barMark.glsl.generated.js +4 -0
- package/esm/shaders/barMark.iface.generated.d.ts +90 -0
- package/esm/shaders/barMark.iface.generated.js +205 -0
- package/esm/shaders/barMark.wgsl.generated.d.ts +1 -0
- package/esm/shaders/barMark.wgsl.generated.js +3 -0
- package/esm/shaders/capsule.js.generated.d.ts +1 -0
- package/esm/shaders/capsule.js.generated.js +11 -0
- package/esm/shaders/clipStrip.generated.d.ts +2 -0
- package/esm/shaders/clipStrip.generated.js +4 -0
- package/esm/shaders/clipStrip.js.generated.d.ts +1 -0
- package/esm/shaders/clipStrip.js.generated.js +21 -0
- package/esm/shaders/colorRampLut.js.generated.d.ts +1 -0
- package/esm/shaders/colorRampLut.js.generated.js +17 -0
- package/esm/shaders/coverageBar.generated.d.ts +2 -3
- package/esm/shaders/coverageBar.generated.js +4 -3
- package/esm/shaders/coverageBar.glsl.generated.d.ts +2 -0
- package/esm/shaders/coverageBar.glsl.generated.js +4 -0
- package/esm/shaders/coverageBar.iface.generated.js +1 -1
- package/esm/shaders/coverageBar.wgsl.generated.d.ts +1 -0
- package/esm/shaders/coverageBar.wgsl.generated.js +3 -0
- package/esm/shaders/coverageClip.generated.d.ts +3 -0
- package/esm/shaders/coverageClip.generated.js +7 -0
- package/esm/shaders/coverageClip.glsl.generated.d.ts +2 -0
- package/esm/shaders/coverageClip.glsl.generated.js +4 -0
- package/esm/shaders/coverageClip.iface.generated.d.ts +85 -0
- package/esm/shaders/coverageClip.iface.generated.js +114 -0
- package/esm/shaders/coverageClip.wgsl.generated.d.ts +1 -0
- package/esm/shaders/coverageClip.wgsl.generated.js +3 -0
- package/esm/shaders/coverageIndicator.generated.d.ts +2 -3
- package/esm/shaders/coverageIndicator.generated.js +4 -3
- package/esm/shaders/coverageIndicator.glsl.generated.d.ts +2 -0
- package/esm/shaders/coverageIndicator.glsl.generated.js +4 -0
- package/esm/shaders/coverageIndicator.iface.generated.js +1 -1
- package/esm/shaders/coverageIndicator.wgsl.generated.d.ts +1 -0
- package/esm/shaders/coverageIndicator.wgsl.generated.js +3 -0
- package/esm/shaders/coverageInterbase.generated.d.ts +2 -3
- package/esm/shaders/coverageInterbase.generated.js +4 -3
- package/esm/shaders/coverageInterbase.glsl.generated.d.ts +2 -0
- package/esm/shaders/coverageInterbase.glsl.generated.js +4 -0
- package/esm/shaders/coverageInterbase.iface.generated.js +1 -1
- package/esm/shaders/coverageInterbase.wgsl.generated.d.ts +1 -0
- package/esm/shaders/coverageInterbase.wgsl.generated.js +3 -0
- package/esm/shaders/coverageMod.generated.d.ts +2 -3
- package/esm/shaders/coverageMod.generated.js +4 -3
- package/esm/shaders/coverageMod.glsl.generated.d.ts +2 -0
- package/esm/shaders/coverageMod.glsl.generated.js +4 -0
- package/esm/shaders/coverageMod.iface.generated.js +1 -1
- package/esm/shaders/coverageMod.wgsl.generated.d.ts +1 -0
- package/esm/shaders/coverageMod.wgsl.generated.js +3 -0
- package/esm/shaders/coverageSnp.generated.d.ts +2 -3
- package/esm/shaders/coverageSnp.generated.js +4 -3
- package/esm/shaders/coverageSnp.glsl.generated.d.ts +2 -0
- package/esm/shaders/coverageSnp.glsl.generated.js +4 -0
- package/esm/shaders/coverageSnp.iface.generated.js +1 -1
- package/esm/shaders/coverageSnp.wgsl.generated.d.ts +1 -0
- package/esm/shaders/coverageSnp.wgsl.generated.js +3 -0
- package/esm/shaders/curveDistance.js.generated.d.ts +5 -0
- package/esm/shaders/curveDistance.js.generated.js +87 -0
- package/esm/shaders/hpmath.js.generated.d.ts +2 -1
- package/esm/shaders/hpmath.js.generated.js +5 -2
- package/esm/shaders/lineCenterMark.generated.d.ts +3 -0
- package/esm/shaders/lineCenterMark.generated.js +7 -0
- package/esm/shaders/lineCenterMark.glsl.generated.d.ts +2 -0
- package/esm/shaders/lineCenterMark.glsl.generated.js +4 -0
- package/esm/shaders/lineCenterMark.iface.generated.d.ts +104 -0
- package/esm/shaders/lineCenterMark.iface.generated.js +237 -0
- package/esm/shaders/lineCenterMark.wgsl.generated.d.ts +1 -0
- package/esm/shaders/lineCenterMark.wgsl.generated.js +3 -0
- package/esm/shaders/lineCommon.generated.d.ts +2 -0
- package/esm/shaders/lineCommon.generated.js +4 -0
- package/esm/shaders/lineStepMark.generated.d.ts +3 -0
- package/esm/shaders/lineStepMark.generated.js +7 -0
- package/esm/shaders/lineStepMark.glsl.generated.d.ts +2 -0
- package/esm/shaders/lineStepMark.glsl.generated.js +4 -0
- package/esm/shaders/lineStepMark.iface.generated.d.ts +98 -0
- package/esm/shaders/lineStepMark.iface.generated.js +225 -0
- package/esm/shaders/lineStepMark.wgsl.generated.d.ts +1 -0
- package/esm/shaders/lineStepMark.wgsl.generated.js +3 -0
- package/esm/shaders/linkMark.consts.generated.d.ts +14 -0
- package/esm/shaders/linkMark.consts.generated.js +16 -0
- package/esm/shaders/linkMark.generated.d.ts +4 -0
- package/esm/shaders/linkMark.generated.js +8 -0
- package/esm/shaders/linkMark.glsl.generated.d.ts +2 -0
- package/esm/shaders/linkMark.glsl.generated.js +4 -0
- package/esm/shaders/linkMark.iface.generated.d.ts +142 -0
- package/esm/shaders/linkMark.iface.generated.js +1829 -0
- package/esm/shaders/linkMark.js.generated.d.ts +7 -0
- package/esm/shaders/linkMark.js.generated.js +159 -0
- package/esm/shaders/linkMark.wgsl.generated.d.ts +1 -0
- package/esm/shaders/linkMark.wgsl.generated.js +3 -0
- package/esm/shaders/markColor.generated.d.ts +9 -0
- package/esm/shaders/markColor.generated.js +11 -0
- package/esm/shaders/pointGlyph.js.generated.d.ts +2 -0
- package/esm/shaders/pointGlyph.js.generated.js +15 -0
- package/esm/shaders/pointMark.consts.generated.d.ts +7 -0
- package/esm/shaders/pointMark.consts.generated.js +9 -0
- package/esm/shaders/pointMark.generated.d.ts +4 -0
- package/esm/shaders/pointMark.generated.js +8 -0
- package/esm/shaders/pointMark.glsl.generated.d.ts +2 -0
- package/esm/shaders/pointMark.glsl.generated.js +4 -0
- package/esm/shaders/pointMark.iface.generated.d.ts +98 -0
- package/esm/shaders/pointMark.iface.generated.js +219 -0
- package/esm/shaders/pointMark.js.generated.d.ts +3 -0
- package/esm/shaders/pointMark.js.generated.js +93 -0
- package/esm/shaders/pointMark.wgsl.generated.d.ts +1 -0
- package/esm/shaders/pointMark.wgsl.generated.js +3 -0
- package/esm/shaders/rowRect.generated.d.ts +0 -1
- package/esm/shaders/rowRect.generated.js +0 -1
- package/esm/shaders/rowTable.generated.d.ts +1 -0
- package/esm/shaders/rowTable.generated.js +3 -0
- package/esm/shaders/rowTable.js.generated.d.ts +5 -0
- package/esm/shaders/rowTable.js.generated.js +29 -0
- package/esm/shaders/scoreScale.generated.d.ts +2 -0
- package/esm/shaders/scoreScale.generated.js +4 -0
- package/esm/shaders/scoreScale.js.generated.d.ts +1 -0
- package/esm/shaders/scoreScale.js.generated.js +50 -23
- package/esm/shaders/spanMark.generated.d.ts +3 -0
- package/esm/shaders/spanMark.generated.js +7 -0
- package/esm/shaders/spanMark.glsl.generated.d.ts +2 -0
- package/esm/shaders/spanMark.glsl.generated.js +4 -0
- package/esm/shaders/spanMark.iface.generated.d.ts +81 -0
- package/esm/shaders/spanMark.iface.generated.js +215 -0
- package/esm/shaders/spanMark.wgsl.generated.d.ts +1 -0
- package/esm/shaders/spanMark.wgsl.generated.js +3 -0
- package/esm/sharedBackendKey.d.ts +18 -0
- package/esm/{keyedRenderingBackend.js → sharedBackendKey.js} +5 -0
- package/esm/slangPass.d.ts +18 -11
- package/esm/slangPass.js +12 -4
- package/esm/useGraphicsCapabilities.d.ts +1 -1
- package/esm/useGraphicsCapabilities.js +1 -1
- package/esm/useRenderingBackend.d.ts +13 -13
- package/esm/useRenderingBackend.js +58 -20
- package/esm/webgpuUtils.d.ts +3 -11
- package/esm/webgpuUtils.js +12 -33
- package/package.json +32 -7
- package/src/shaders/antialias.slang +46 -0
- package/src/shaders/arcHull.test.ts +267 -0
- package/src/shaders/barMark.generated.ts +11 -0
- package/src/shaders/barMark.glsl.generated.ts +6 -0
- package/src/shaders/barMark.iface.generated.ts +285 -0
- package/src/shaders/barMark.slang +167 -0
- package/src/shaders/barMark.wgsl.generated.ts +4 -0
- package/src/shaders/capsule.js.generated.ts +14 -0
- package/src/shaders/capsule.slang +12 -4
- package/src/shaders/clipStrip.generated.ts +6 -0
- package/src/shaders/clipStrip.js.generated.ts +22 -0
- package/src/shaders/clipStrip.slang +34 -0
- package/src/shaders/clipStrip.test.ts +39 -0
- package/src/shaders/colorPack.slang +5 -1
- package/src/shaders/colorRampLut.js.generated.ts +22 -0
- package/src/shaders/colorRampLut.slang +15 -1
- package/src/shaders/coverageBand.slang +64 -32
- package/src/shaders/coverageBar.generated.ts +6 -5
- package/src/shaders/coverageBar.glsl.generated.ts +6 -0
- package/src/shaders/coverageBar.iface.generated.ts +1 -1
- package/src/shaders/coverageBar.slang +10 -10
- package/src/shaders/coverageBar.wgsl.generated.ts +4 -0
- package/src/shaders/coverageClip.generated.ts +11 -0
- package/src/shaders/coverageClip.glsl.generated.ts +6 -0
- package/src/shaders/coverageClip.iface.generated.ts +172 -0
- package/src/shaders/coverageClip.slang +38 -0
- package/src/shaders/coverageClip.wgsl.generated.ts +4 -0
- package/src/shaders/coverageIndicator.generated.ts +6 -5
- package/src/shaders/coverageIndicator.glsl.generated.ts +6 -0
- package/src/shaders/coverageIndicator.iface.generated.ts +1 -1
- package/src/shaders/coverageIndicator.wgsl.generated.ts +4 -0
- package/src/shaders/coverageInterbase.generated.ts +6 -5
- package/src/shaders/coverageInterbase.glsl.generated.ts +6 -0
- package/src/shaders/coverageInterbase.iface.generated.ts +1 -1
- package/src/shaders/coverageInterbase.wgsl.generated.ts +4 -0
- package/src/shaders/coverageMod.generated.ts +6 -5
- package/src/shaders/coverageMod.glsl.generated.ts +6 -0
- package/src/shaders/coverageMod.iface.generated.ts +1 -1
- package/src/shaders/coverageMod.wgsl.generated.ts +4 -0
- package/src/shaders/coverageSnp.generated.ts +6 -5
- package/src/shaders/coverageSnp.glsl.generated.ts +6 -0
- package/src/shaders/coverageSnp.iface.generated.ts +1 -1
- package/src/shaders/coverageSnp.wgsl.generated.ts +4 -0
- package/src/shaders/curveDistance.js.generated.ts +91 -0
- package/src/shaders/curveDistance.slang +265 -0
- package/src/shaders/hpClampFloor.test.ts +87 -0
- package/src/shaders/hpmath.js.generated.ts +6 -2
- package/src/shaders/hpmath.slang +100 -48
- package/src/shaders/insetValueScale.test.ts +67 -0
- package/src/shaders/lineCenterMark.generated.ts +11 -0
- package/src/shaders/lineCenterMark.glsl.generated.ts +6 -0
- package/src/shaders/lineCenterMark.iface.generated.ts +328 -0
- package/src/shaders/lineCenterMark.slang +127 -0
- package/src/shaders/lineCenterMark.wgsl.generated.ts +4 -0
- package/src/shaders/lineCommon.generated.ts +6 -0
- package/src/shaders/lineCommon.slang +102 -0
- package/src/shaders/lineStepMark.generated.ts +11 -0
- package/src/shaders/lineStepMark.glsl.generated.ts +6 -0
- package/src/shaders/lineStepMark.iface.generated.ts +311 -0
- package/src/shaders/lineStepMark.slang +125 -0
- package/src/shaders/lineStepMark.wgsl.generated.ts +4 -0
- package/src/shaders/linkMark.consts.generated.ts +30 -0
- package/src/shaders/linkMark.generated.ts +12 -0
- package/src/shaders/linkMark.glsl.generated.ts +6 -0
- package/src/shaders/linkMark.iface.generated.ts +1954 -0
- package/src/shaders/linkMark.js.generated.ts +167 -0
- package/src/shaders/linkMark.slang +439 -0
- package/src/shaders/linkMark.wgsl.generated.ts +4 -0
- package/src/shaders/markColor.generated.ts +20 -0
- package/src/shaders/markColor.slang +128 -0
- package/src/shaders/pointGlyph.js.generated.ts +20 -0
- package/src/shaders/pointGlyph.slang +23 -7
- package/src/shaders/pointGlyphPad.test.ts +242 -0
- package/src/shaders/pointMark.consts.generated.ts +16 -0
- package/src/shaders/pointMark.generated.ts +12 -0
- package/src/shaders/pointMark.glsl.generated.ts +6 -0
- package/src/shaders/pointMark.iface.generated.ts +304 -0
- package/src/shaders/pointMark.js.generated.ts +99 -0
- package/src/shaders/pointMark.slang +227 -0
- package/src/shaders/pointMark.wgsl.generated.ts +4 -0
- package/src/shaders/rowRect.generated.ts +0 -2
- package/src/shaders/rowRect.slang +39 -58
- package/src/shaders/rowTable.generated.ts +4 -0
- package/src/shaders/rowTable.js.generated.ts +37 -0
- package/src/shaders/rowTable.slang +98 -0
- package/src/shaders/scoreScale.generated.ts +6 -0
- package/src/shaders/scoreScale.js.generated.ts +48 -23
- package/src/shaders/scoreScale.slang +49 -55
- package/src/shaders/sdEllipse.test.ts +211 -0
- package/src/shaders/spanMark.generated.ts +11 -0
- package/src/shaders/spanMark.glsl.generated.ts +6 -0
- package/src/shaders/spanMark.iface.generated.ts +289 -0
- package/src/shaders/spanMark.slang +77 -0
- package/src/shaders/spanMark.wgsl.generated.ts +4 -0
- package/src/shaders/valueScale.slang +26 -0
- package/src/shaders/wideCircleLeg.test.ts +85 -0
- package/esm/globalRenderingBackend.d.ts +0 -90
- package/esm/globalRenderingBackend.js +0 -53
- package/esm/keyedRenderingBackend.d.ts +0 -41
package/esm/hal/webgpuHal.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/// <reference types="@webgpu/types" />
|
|
2
|
-
import { syncCanvasSize } from '../canvas2dUtils.js';
|
|
2
|
+
import { getDpr, syncCanvasSize } from '../canvas2dUtils.js';
|
|
3
3
|
import { canvasConfiguredBy, canvasContextError, noteCanvasConfigured, noteCanvasContext, } from '../canvasContext.js';
|
|
4
|
+
import { createGpuSurfaceLostError } from '../gpuContextLostError.js';
|
|
4
5
|
import { getGpuDevice } from '../gpuDevice.js';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
6
|
+
import { createVertexBuffer } from '../webgpuUtils.js';
|
|
7
|
+
import { bindGroupLayoutEntries, getOrBuildPipeline, getPassLayout, pipelineRecipe, } from './deviceGpuCache.js';
|
|
7
8
|
import { GpuHalBase } from './gpuHalBase.js';
|
|
8
9
|
class ShaderCompileError extends Error {
|
|
9
10
|
constructor(passId, details) {
|
|
@@ -11,21 +12,15 @@ class ShaderCompileError extends Error {
|
|
|
11
12
|
this.name = 'ShaderCompileError';
|
|
12
13
|
}
|
|
13
14
|
}
|
|
14
|
-
//
|
|
15
|
-
//
|
|
15
|
+
// Each writeUniforms() call in a frame occupies one aligned slot in the uniform
|
|
16
|
+
// ring, a GPU buffer and a CPU staging array per display, each slot sized by
|
|
17
|
+
// the largest struct a registered pass declares. A frame is measured using 2-4
|
|
18
|
+
// slots, so a ring starts at INITIAL_UNIFORM_SLOTS and doubles when a frame asks
|
|
19
|
+
// for more: agent-docs/reference/ARCHITECTURAL_LIMITS.md §"The uniform ring".
|
|
16
20
|
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
|
|
20
|
-
// count. A display whose uniform fits the 256-byte minimum alignment pays
|
|
21
|
-
// 512 KB each side; alignments' 864-byte uniform aligns to 1024, so 2 MiB each
|
|
22
|
-
// side. Trivial once, per-track at ten open alignments tracks.
|
|
23
|
-
//
|
|
24
|
-
// Exhausting it does not throw: the write is dropped and its draws render
|
|
25
|
-
// against the previous batch's uniforms, which is wrong data rather than stale
|
|
26
|
-
// data. If we ever hit the cap, switch to a dynamic-growth buffer (recreate
|
|
27
|
-
// buffer + every region's bind group) rather than just bumping the constant
|
|
28
|
-
// again.
|
|
21
|
+
// Past MAX_UNIFORM_SLOTS a write is dropped and its draws render against
|
|
22
|
+
// another batch's uniforms, without an error.
|
|
23
|
+
const INITIAL_UNIFORM_SLOTS = 16;
|
|
29
24
|
const MAX_UNIFORM_SLOTS = 2048;
|
|
30
25
|
// Warn while there is still headroom, because the cap itself is not a place to
|
|
31
26
|
// find out. A renderer's per-frame write count is rarely one number: alignments
|
|
@@ -35,58 +30,22 @@ const MAX_UNIFORM_SLOTS = 2048;
|
|
|
35
30
|
// the "~50 writes/frame" this file used to claim, and nothing reported the
|
|
36
31
|
// difference between 50 and 1900.
|
|
37
32
|
const UNIFORM_SLOT_WARN_AT = MAX_UNIFORM_SLOTS / 2;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
// BlendOperation::Max"). Same blend as webgl2Hal's bare glBlendEquation(MAX).
|
|
43
|
-
const max = { srcFactor: 'one', dstFactor: 'one', operation: 'max' };
|
|
44
|
-
// Otherwise RGB uses the caller-supplied factors and alpha always accumulates
|
|
45
|
-
// through ONE / ONE_MINUS_SRC_ALPHA (matches webgl2Hal.applyBlendState).
|
|
46
|
-
return bs.op === 'max'
|
|
47
|
-
? { color: max, alpha: max }
|
|
48
|
-
: {
|
|
49
|
-
color: { srcFactor: bs.srcFactor, dstFactor: bs.dstFactor },
|
|
50
|
-
alpha: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha' },
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
// Every pass reads per-instance data from a vertex buffer (no storage binding
|
|
54
|
-
// at 0) because Slang-generated shaders cross-compile to GLSL ES, which has no
|
|
55
|
-
// SSBOs. The two @group(0) layouts a pass is built against — uniform-only, and
|
|
56
|
-
// uniform + texture + sampler — belong to the device, not to this HAL, and live
|
|
57
|
-
// in `deviceGpuCache.ts` with the pipelines.
|
|
58
|
-
async function buildPipeline(device, desc, layouts, sampleCount) {
|
|
59
|
-
const module = device.createShaderModule({ code: desc.wgslSource });
|
|
33
|
+
// Every pass reads per-instance data from a vertex buffer, because Slang's
|
|
34
|
+
// GLSL ES target has no SSBOs.
|
|
35
|
+
async function buildPipeline(device, recipe, passId) {
|
|
36
|
+
const module = device.createShaderModule({ code: recipe.wgslSource });
|
|
60
37
|
const info = await module.getCompilationInfo();
|
|
61
38
|
const errors = info.messages.filter(m => m.type === 'error');
|
|
62
39
|
if (errors.length > 0) {
|
|
63
40
|
const details = errors
|
|
64
41
|
.map(m => `line ${m.lineNum}: ${m.message}`)
|
|
65
42
|
.join('; ');
|
|
66
|
-
throw new ShaderCompileError(
|
|
67
|
-
}
|
|
68
|
-
const blend =
|
|
69
|
-
? desc.blendState
|
|
70
|
-
? gpuBlendState(desc.blendState)
|
|
71
|
-
: STANDARD_BLEND_STATE
|
|
72
|
-
: undefined;
|
|
73
|
-
// Every pass feeds @location(N) inputs from a bound vertex buffer.
|
|
74
|
-
const vertexBuffers = [
|
|
75
|
-
{
|
|
76
|
-
arrayStride: desc.instanceStride,
|
|
77
|
-
stepMode: 'instance',
|
|
78
|
-
attributes: desc.vertexAttributes.map((attr, i) => ({
|
|
79
|
-
shaderLocation: i,
|
|
80
|
-
offset: attr.offsetBytes,
|
|
81
|
-
format: toGpuVertexFormat(attr),
|
|
82
|
-
})),
|
|
83
|
-
},
|
|
84
|
-
];
|
|
43
|
+
throw new ShaderCompileError(passId, details);
|
|
44
|
+
}
|
|
45
|
+
const { blend } = recipe;
|
|
85
46
|
return device.createRenderPipelineAsync({
|
|
86
|
-
layout:
|
|
87
|
-
|
|
88
|
-
: layouts.uniformOnlyPipelineLayout,
|
|
89
|
-
vertex: { module, entryPoint: 'vs_main', buffers: vertexBuffers },
|
|
47
|
+
layout: getPassLayout(device, recipe.bindGroupLayout).pipelineLayout,
|
|
48
|
+
vertex: { module, entryPoint: 'vs_main', buffers: [recipe.vertexBuffer] },
|
|
90
49
|
fragment: {
|
|
91
50
|
module,
|
|
92
51
|
entryPoint: 'fs_main',
|
|
@@ -97,17 +56,18 @@ async function buildPipeline(device, desc, layouts, sampleCount) {
|
|
|
97
56
|
},
|
|
98
57
|
],
|
|
99
58
|
},
|
|
100
|
-
primitive: { topology:
|
|
101
|
-
multisample: sampleCount === 1 ? undefined : { count: sampleCount },
|
|
59
|
+
primitive: { topology: recipe.topology },
|
|
60
|
+
multisample: recipe.sampleCount === 1 ? undefined : { count: recipe.sampleCount },
|
|
102
61
|
});
|
|
103
62
|
}
|
|
104
|
-
//
|
|
105
|
-
//
|
|
106
|
-
//
|
|
107
|
-
// (`getPass`) and this side does not; see ARCHITECTURAL_LIMITS.md §"Every
|
|
108
|
-
// WebGPU display resolves its whole pass list before it can paint".
|
|
63
|
+
// Every declared pass up front, unlike WebGL2's first-draw link; see
|
|
64
|
+
// ARCHITECTURAL_LIMITS.md §"Every WebGPU display resolves its whole pass list
|
|
65
|
+
// before it can paint". Each pass compiles as soon as its own WGSL arrives.
|
|
109
66
|
async function resolvePipelines(device, descriptors, sampleCount) {
|
|
110
|
-
const built = await Promise.all(descriptors.map(
|
|
67
|
+
const built = await Promise.all(descriptors.map(async (desc) => {
|
|
68
|
+
const { WGSL_SOURCE } = await desc.source.wgsl();
|
|
69
|
+
return getOrBuildPipeline(device, pipelineRecipe(desc, WGSL_SOURCE, sampleCount), recipe => buildPipeline(device, recipe, desc.id));
|
|
70
|
+
}));
|
|
111
71
|
return new Map(descriptors.map((desc, i) => [desc.id, built[i]]));
|
|
112
72
|
}
|
|
113
73
|
// Behavioral parity with WebGL2Hal is enforced by tests, not by this file:
|
|
@@ -124,30 +84,30 @@ export class WebGPUHal extends GpuHalBase {
|
|
|
124
84
|
canvas;
|
|
125
85
|
context;
|
|
126
86
|
pipelines;
|
|
87
|
+
// Each pass's textures, by the sampler that reads each.
|
|
127
88
|
passTextures = new Map();
|
|
128
|
-
//
|
|
129
|
-
//
|
|
130
|
-
//
|
|
89
|
+
// Every pass's bind group, by the pass drawn. A pass that binds no texture
|
|
90
|
+
// has one from construction, shared with each pass over the same layout; a
|
|
91
|
+
// textured pass gets its own when its texture arrives, dropped when
|
|
92
|
+
// `uploadTexture` replaces the texture. The layouts are the device's, shared
|
|
93
|
+
// with every display on it; the groups are this HAL's, since they name its
|
|
94
|
+
// uniform ring buffer.
|
|
131
95
|
passBindGroups = new Map();
|
|
132
|
-
// The device's layouts, not this HAL's — shared with every other display on
|
|
133
|
-
// the same device. The bind GROUPS above stay per-HAL: they reference this
|
|
134
|
-
// HAL's uniform ring buffer.
|
|
135
|
-
layouts;
|
|
136
96
|
// Uniform ring buffer: holds up to MAX_UNIFORM_SLOTS sets of uniforms so
|
|
137
97
|
// that all draw calls in a frame can reference different uniform data via
|
|
138
98
|
// dynamic offsets, enabling a single command encoder + submit per frame.
|
|
139
99
|
alignedUniformSize;
|
|
140
100
|
uniformRingBuffer;
|
|
141
101
|
uniformStaging;
|
|
102
|
+
uniformSlots = INITIAL_UNIFORM_SLOTS;
|
|
142
103
|
uniformSlot = 0;
|
|
104
|
+
// Rings this frame outgrew, each with the staged bytes its encoded draws
|
|
105
|
+
// read: written at submit, released after it.
|
|
106
|
+
outgrownRings = [];
|
|
143
107
|
// Once per HAL, not once per frame: at 60fps a per-frame warning is a
|
|
144
108
|
// console the developer stops reading, and the fact is about the renderer
|
|
145
109
|
// rather than about this frame.
|
|
146
110
|
warnedUniformSlots = false;
|
|
147
|
-
// Shared bind group for every uniform-only pass — only references
|
|
148
|
-
// `uniformRingBuffer` (via dynamic offset at draw time), so one instance
|
|
149
|
-
// serves all passes/regions instead of allocating a fresh one per upload.
|
|
150
|
-
uniformOnlyBindGroup;
|
|
151
111
|
// Samples per pixel this display renders at, stated by whoever built it (see
|
|
152
112
|
// `RenderingBackendOptions.sampleCount`). Every render-pass, texture and
|
|
153
113
|
// pipeline decision below reads it, so the two shapes cannot mismatch — and
|
|
@@ -175,7 +135,7 @@ export class WebGPUHal extends GpuHalBase {
|
|
|
175
135
|
* selection spanning two sections re-uploads `OVERLAY_REGION` once per
|
|
176
136
|
* section, inside the block loop.
|
|
177
137
|
*
|
|
178
|
-
* Deferring
|
|
138
|
+
* Deferring the destroy makes mid-frame replacement legal instead of merely
|
|
179
139
|
* warned about; synteny's `ensureUploaded` has always deleted a pass mid-frame
|
|
180
140
|
* that the open pass never referenced, and that case was never the bug.
|
|
181
141
|
*/
|
|
@@ -196,26 +156,93 @@ export class WebGPUHal extends GpuHalBase {
|
|
|
196
156
|
// than once per frame, and a reconfigure that failed is not attempted again.
|
|
197
157
|
warnedSwapChainLoss = false;
|
|
198
158
|
swapChainUnrecoverable = false;
|
|
199
|
-
|
|
159
|
+
reportedSurfaceFailure = false;
|
|
160
|
+
constructor(device, canvas, context, descriptors, sampleCount, pipelines) {
|
|
200
161
|
super(descriptors, 'WebGPUHal');
|
|
201
162
|
this.device = device;
|
|
202
163
|
this.canvas = canvas;
|
|
203
164
|
this.context = context;
|
|
204
165
|
this.pipelines = pipelines;
|
|
205
|
-
this.layouts = layouts;
|
|
206
166
|
this.sampleCount = sampleCount;
|
|
207
167
|
// Align uniform slots to device requirements for dynamic offsets
|
|
208
168
|
const alignment = device.limits.minUniformBufferOffsetAlignment;
|
|
209
|
-
this.alignedUniformSize =
|
|
210
|
-
|
|
211
|
-
this.
|
|
212
|
-
|
|
213
|
-
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
214
|
-
});
|
|
169
|
+
this.alignedUniformSize =
|
|
170
|
+
Math.ceil(this.uniformByteSize / alignment) * alignment;
|
|
171
|
+
const ringSize = this.uniformSlots * this.alignedUniformSize;
|
|
172
|
+
this.uniformRingBuffer = this.createRing(ringSize);
|
|
215
173
|
this.uniformStaging = new Uint8Array(ringSize);
|
|
216
|
-
this.
|
|
174
|
+
this.bindUntexturedPasses();
|
|
217
175
|
this.configureContext();
|
|
218
176
|
}
|
|
177
|
+
createRing(size) {
|
|
178
|
+
return this.device.createBuffer({
|
|
179
|
+
size,
|
|
180
|
+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
// Every pass's bind group names the ring buffer, so a new ring rebinds them:
|
|
184
|
+
// an untextured pass here, a textured one on its next draw (`bindTexture`).
|
|
185
|
+
bindUntexturedPasses() {
|
|
186
|
+
const byLayout = new Map();
|
|
187
|
+
for (const desc of this.descriptors.values()) {
|
|
188
|
+
if (desc.bindings.some(b => b.kind === 'texture')) {
|
|
189
|
+
this.passBindGroups.delete(desc.id);
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
const layout = this.bindGroupLayoutOf(desc.bindings);
|
|
193
|
+
let group = byLayout.get(layout);
|
|
194
|
+
if (!group) {
|
|
195
|
+
group = this.createBindGroup(desc.bindings, layout);
|
|
196
|
+
byLayout.set(layout, group);
|
|
197
|
+
}
|
|
198
|
+
this.passBindGroups.set(desc.id, group);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// Doubles the ring mid-frame. The draws already encoded bind the old buffer,
|
|
202
|
+
// so it takes the slots staged so far at submit and is released after.
|
|
203
|
+
growRing() {
|
|
204
|
+
const bytes = this.uniformSlot * this.alignedUniformSize;
|
|
205
|
+
this.outgrownRings.push({ buffer: this.uniformRingBuffer, bytes });
|
|
206
|
+
this.destroyWhenIdle(this.uniformRingBuffer);
|
|
207
|
+
this.uniformSlots = Math.min(this.uniformSlots * 2, MAX_UNIFORM_SLOTS);
|
|
208
|
+
const size = this.uniformSlots * this.alignedUniformSize;
|
|
209
|
+
this.uniformRingBuffer = this.createRing(size);
|
|
210
|
+
const staging = new Uint8Array(size);
|
|
211
|
+
staging.set(this.uniformStaging.subarray(0, bytes));
|
|
212
|
+
this.uniformStaging = staging;
|
|
213
|
+
this.bindUntexturedPasses();
|
|
214
|
+
}
|
|
215
|
+
bindGroupLayoutOf(bindings) {
|
|
216
|
+
return getPassLayout(this.device, bindGroupLayoutEntries(bindings))
|
|
217
|
+
.bindGroupLayout;
|
|
218
|
+
}
|
|
219
|
+
// One entry per binding in the pass's table, the same table its layout was
|
|
220
|
+
// built from, so the group cannot name a binding the layout lacks.
|
|
221
|
+
createBindGroup(bindings, layout, textures) {
|
|
222
|
+
return this.device.createBindGroup({
|
|
223
|
+
layout,
|
|
224
|
+
entries: bindings.map(b => ({
|
|
225
|
+
binding: b.index,
|
|
226
|
+
resource: this.bindingResource(b, textures?.get(b.name)),
|
|
227
|
+
})),
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
bindingResource(b, texture) {
|
|
231
|
+
if (b.kind === 'uniform') {
|
|
232
|
+
return {
|
|
233
|
+
buffer: this.uniformRingBuffer,
|
|
234
|
+
offset: 0,
|
|
235
|
+
size: this.alignedUniformSize,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
if (texture && b.kind === 'texture') {
|
|
239
|
+
return texture.texture.createView();
|
|
240
|
+
}
|
|
241
|
+
if (texture && b.kind === 'sampler') {
|
|
242
|
+
return texture.sampler;
|
|
243
|
+
}
|
|
244
|
+
throw new Error(`[WebGPUHal] nothing to bind for the ${b.kind} '${b.name}' at binding ${b.index}`);
|
|
245
|
+
}
|
|
219
246
|
limits() {
|
|
220
247
|
const { maxBufferSize, maxTextureDimension2D } = this.device.limits;
|
|
221
248
|
return {
|
|
@@ -245,17 +272,16 @@ export class WebGPUHal extends GpuHalBase {
|
|
|
245
272
|
});
|
|
246
273
|
noteCanvasConfigured(this.canvas, this);
|
|
247
274
|
}
|
|
248
|
-
static async create(canvas, descriptors,
|
|
275
|
+
static async create(canvas, descriptors, sampleCount) {
|
|
249
276
|
const device = await getGpuDevice();
|
|
250
277
|
if (!device) {
|
|
251
278
|
return null;
|
|
252
279
|
}
|
|
253
280
|
// Resolve pipelines BEFORE acquiring the canvas's webgpu context. A canvas's
|
|
254
|
-
// context type is permanent once acquired, so if shader
|
|
255
|
-
// here the canvas stays pristine and createGpuHal's WebGL2 fallback
|
|
256
|
-
// still claim it — otherwise a partial WebGPU init would drop us all the
|
|
257
|
-
// to Canvas2D on a WebGL2-capable machine.
|
|
258
|
-
const layouts = getDeviceLayouts(device);
|
|
281
|
+
// context type is permanent once acquired, so if a shader fails to load or
|
|
282
|
+
// compile here the canvas stays pristine and createGpuHal's WebGL2 fallback
|
|
283
|
+
// can still claim it — otherwise a partial WebGPU init would drop us all the
|
|
284
|
+
// way to Canvas2D on a WebGL2-capable machine.
|
|
259
285
|
const pipelines = await resolvePipelines(device, descriptors, sampleCount);
|
|
260
286
|
const context = canvas.getContext('webgpu');
|
|
261
287
|
if (!context) {
|
|
@@ -266,9 +292,14 @@ export class WebGPUHal extends GpuHalBase {
|
|
|
266
292
|
throw canvasContextError(canvas, 'webgpu');
|
|
267
293
|
}
|
|
268
294
|
noteCanvasContext(canvas, 'webgpu');
|
|
269
|
-
return new WebGPUHal(device, canvas, context, descriptors,
|
|
295
|
+
return new WebGPUHal(device, canvas, context, descriptors, sampleCount, pipelines);
|
|
270
296
|
}
|
|
271
297
|
resize(width, height) {
|
|
298
|
+
// A render autorun can fire between `dispose` and the model dropping its
|
|
299
|
+
// backend reference, and nothing destroys what a disposed HAL allocates.
|
|
300
|
+
if (this.disposed) {
|
|
301
|
+
return { x: getDpr(), y: getDpr() };
|
|
302
|
+
}
|
|
272
303
|
const { changed, scale } = syncCanvasSize(this.canvas, width, height);
|
|
273
304
|
// `!this.msaaTexture` covers the first frame and a rebuild that bailed. At
|
|
274
305
|
// sampleCount 1 there is never a texture, so without the count in the
|
|
@@ -279,6 +310,11 @@ export class WebGPUHal extends GpuHalBase {
|
|
|
279
310
|
}
|
|
280
311
|
return scale;
|
|
281
312
|
}
|
|
313
|
+
releaseRenderTargets() {
|
|
314
|
+
if (!this.disposed) {
|
|
315
|
+
this.recreateMsaaTexture(0, 0);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
282
318
|
recreateMsaaTexture(width, height) {
|
|
283
319
|
this.msaaTexture?.destroy();
|
|
284
320
|
this.msaaTexture = null;
|
|
@@ -309,50 +345,32 @@ export class WebGPUHal extends GpuHalBase {
|
|
|
309
345
|
* (and a dropped draw) the moment one side samples a texture.
|
|
310
346
|
*
|
|
311
347
|
* Nothing in a bind group varies per region either: it references the
|
|
312
|
-
* HAL-wide uniform ring buffer plus the pass's own texture/sampler.
|
|
313
|
-
*
|
|
314
|
-
*
|
|
315
|
-
* Returns undefined when a pass needs a texture that hasn't arrived yet;
|
|
316
|
-
* drawPass skips those.
|
|
348
|
+
* HAL-wide uniform ring buffer plus the pass's own texture/sampler. Returns
|
|
349
|
+
* undefined when a pass needs a texture that hasn't arrived yet; drawPass
|
|
350
|
+
* skips those.
|
|
317
351
|
*/
|
|
318
352
|
getBindGroup(passId) {
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
binding: 1,
|
|
330
|
-
resource: {
|
|
331
|
-
buffer: this.uniformRingBuffer,
|
|
332
|
-
offset: 0,
|
|
333
|
-
size: this.alignedUniformSize,
|
|
334
|
-
},
|
|
335
|
-
},
|
|
336
|
-
{
|
|
337
|
-
binding: tb.textureBinding,
|
|
338
|
-
resource: texState.texture.createView(),
|
|
339
|
-
},
|
|
340
|
-
{ binding: tb.samplerBinding, resource: texState.sampler },
|
|
341
|
-
],
|
|
342
|
-
});
|
|
343
|
-
this.passBindGroups.set(passId, bindGroup);
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
return bindGroup;
|
|
353
|
+
return this.passBindGroups.get(passId) ?? this.bindTexture(passId);
|
|
354
|
+
}
|
|
355
|
+
// Built once every texture the pass samples has arrived.
|
|
356
|
+
bindTexture(passId) {
|
|
357
|
+
const textures = this.passTextures.get(passId);
|
|
358
|
+
const desc = this.descriptors.get(passId);
|
|
359
|
+
if (!textures ||
|
|
360
|
+
!desc ||
|
|
361
|
+
desc.bindings.some(b => b.kind === 'texture' && !textures.has(b.name))) {
|
|
362
|
+
return undefined;
|
|
347
363
|
}
|
|
348
|
-
|
|
364
|
+
const group = this.createBindGroup(desc.bindings, this.bindGroupLayoutOf(desc.bindings), textures);
|
|
365
|
+
this.passBindGroups.set(passId, group);
|
|
366
|
+
return group;
|
|
349
367
|
}
|
|
350
368
|
/**
|
|
351
369
|
* The one place a GPU resource this HAL owns is released — see
|
|
352
370
|
* {@link pendingDestroy} for why the frame decides when.
|
|
353
371
|
*
|
|
354
372
|
* Every buffer release routes here through the `RegionRegistry` destroy hook,
|
|
355
|
-
* so `uploadBuffer`, `deleteBuffer`, `deleteRegion
|
|
373
|
+
* so `uploadBuffer`, `deleteBuffer`, `deleteRegion` and
|
|
356
374
|
* `dispose` are all covered without any of them knowing a frame is open.
|
|
357
375
|
*/
|
|
358
376
|
destroyWhenIdle(resource) {
|
|
@@ -370,29 +388,42 @@ export class WebGPUHal extends GpuHalBase {
|
|
|
370
388
|
this.pendingDestroy.length = 0;
|
|
371
389
|
}
|
|
372
390
|
createTexture(passId, binding, data, width, height) {
|
|
373
|
-
|
|
391
|
+
let byName = this.passTextures.get(passId);
|
|
392
|
+
if (!byName) {
|
|
393
|
+
byName = new Map();
|
|
394
|
+
this.passTextures.set(passId, byName);
|
|
395
|
+
}
|
|
396
|
+
const existing = byName.get(binding.name);
|
|
374
397
|
if (existing) {
|
|
375
|
-
// Same hazard as a buffer,
|
|
376
|
-
// `uploadTexture` in tree is a colour ramp
|
|
377
|
-
//
|
|
378
|
-
//
|
|
379
|
-
//
|
|
380
|
-
//
|
|
398
|
+
// Same hazard as a buffer, and no longer a hypothetical one: every
|
|
399
|
+
// `uploadTexture` in tree is a colour ramp, and since the ramp became a
|
|
400
|
+
// `defineMark` `texture` they are all written by `GpuMarkBackend`'s
|
|
401
|
+
// `bindRamp` from inside `drawRegion` — mid-frame, between an encoded
|
|
402
|
+
// draw and its submit. The deferral is what makes replacing one there
|
|
403
|
+
// safe.
|
|
381
404
|
this.destroyWhenIdle(existing.texture);
|
|
382
405
|
}
|
|
406
|
+
const bytes = data instanceof Uint8Array;
|
|
383
407
|
const texture = this.device.createTexture({
|
|
384
408
|
size: [width, height],
|
|
385
409
|
format: 'rgba8unorm',
|
|
386
|
-
usage: GPUTextureUsage.TEXTURE_BINDING |
|
|
410
|
+
usage: GPUTextureUsage.TEXTURE_BINDING |
|
|
411
|
+
GPUTextureUsage.COPY_DST |
|
|
412
|
+
(bytes ? 0 : GPUTextureUsage.RENDER_ATTACHMENT),
|
|
387
413
|
});
|
|
388
|
-
|
|
414
|
+
if (bytes) {
|
|
415
|
+
this.device.queue.writeTexture({ texture }, data, { bytesPerRow: width * 4 }, { width, height });
|
|
416
|
+
}
|
|
417
|
+
else {
|
|
418
|
+
this.device.queue.copyExternalImageToTexture({ source: data }, { texture, premultipliedAlpha: true }, { width, height });
|
|
419
|
+
}
|
|
389
420
|
const sampler = this.device.createSampler({
|
|
390
421
|
magFilter: binding.filter,
|
|
391
422
|
minFilter: binding.filter,
|
|
392
423
|
addressModeU: 'clamp-to-edge',
|
|
393
424
|
addressModeV: 'clamp-to-edge',
|
|
394
425
|
});
|
|
395
|
-
|
|
426
|
+
byName.set(binding.name, { texture, sampler });
|
|
396
427
|
// Drop the cached bind group so the next draw rebuilds it against the new
|
|
397
428
|
// texture (and so a pass drawn before its first texture arrived stops
|
|
398
429
|
// being skipped).
|
|
@@ -401,14 +432,17 @@ export class WebGPUHal extends GpuHalBase {
|
|
|
401
432
|
writeUniforms(data) {
|
|
402
433
|
if (this.currentEncoder) {
|
|
403
434
|
// Inside a frame: stage data at the current slot for batched upload
|
|
404
|
-
if (this.uniformSlot >=
|
|
435
|
+
if (this.uniformSlot >= this.uniformSlots &&
|
|
436
|
+
this.uniformSlots < MAX_UNIFORM_SLOTS) {
|
|
437
|
+
this.growRing();
|
|
438
|
+
}
|
|
439
|
+
if (this.uniformSlot >= this.uniformSlots) {
|
|
405
440
|
console.error(`[WebGPUHal] uniform ring buffer exhausted at ${MAX_UNIFORM_SLOTS} ` +
|
|
406
441
|
`writeUniforms calls in one frame — this write is dropped, so the ` +
|
|
407
442
|
`paired draw renders with the previous batch's uniforms (wrong ` +
|
|
408
443
|
`data, not last-frame-stale). This indicates a renderer doing far ` +
|
|
409
444
|
`more per-frame uniform writes than expected; investigate the call ` +
|
|
410
|
-
`site before raising the cap
|
|
411
|
-
`dynamic-growth ring buffer).`);
|
|
445
|
+
`site before raising the cap.`);
|
|
412
446
|
return;
|
|
413
447
|
}
|
|
414
448
|
const offset = this.uniformSlot * this.alignedUniformSize;
|
|
@@ -471,6 +505,21 @@ export class WebGPUHal extends GpuHalBase {
|
|
|
471
505
|
}
|
|
472
506
|
}
|
|
473
507
|
}
|
|
508
|
+
/**
|
|
509
|
+
* Once per HAL, not once per frame: the refusal holds for every frame at this
|
|
510
|
+
* canvas size, and dotplot and synteny keep their canvas mounted through a
|
|
511
|
+
* `renderError`, so a per-frame report there never stops.
|
|
512
|
+
*
|
|
513
|
+
* Not terminal — a refusal tracks the canvas dimensions, so a resize can clear
|
|
514
|
+
* it. `swapChainUnrecoverable` stays for the case it was written for.
|
|
515
|
+
*/
|
|
516
|
+
reportSurfaceFailure(message) {
|
|
517
|
+
if (this.reportedSurfaceFailure) {
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
this.reportedSurfaceFailure = true;
|
|
521
|
+
this.oom.reportClassified(createGpuSurfaceLostError(message));
|
|
522
|
+
}
|
|
474
523
|
beginFrame(clearR, clearG, clearB, clearA = 1) {
|
|
475
524
|
// Skip the frame entirely rather than encode one that cannot be valid.
|
|
476
525
|
// Zero-size canvas: nothing to draw. Missing MSAA target while MSAA is
|
|
@@ -486,10 +535,23 @@ export class WebGPUHal extends GpuHalBase {
|
|
|
486
535
|
(this.sampleCount > 1 && !this.msaaView)) {
|
|
487
536
|
return;
|
|
488
537
|
}
|
|
489
|
-
//
|
|
490
|
-
//
|
|
491
|
-
//
|
|
538
|
+
// A driver can refuse the canvas image without throwing — Dawn returns an
|
|
539
|
+
// error texture and an error view, and the frame encodes against it — so the
|
|
540
|
+
// acquisition needs a scope, and its own rather than the frame's: those stay
|
|
541
|
+
// pushed iff an encoder is created, and one left on the device's stack
|
|
542
|
+
// swallows every later frame's errors. Popped before the guard below, so
|
|
543
|
+
// every path balances. Awaiting the pop here would suspend across a
|
|
544
|
+
// GPU-process round trip and destroy the canvas texture mid-frame.
|
|
545
|
+
this.device.pushErrorScope('validation');
|
|
492
546
|
const textureView = this.acquireTextureView();
|
|
547
|
+
void this.device
|
|
548
|
+
.popErrorScope()
|
|
549
|
+
.then(err => {
|
|
550
|
+
if (err) {
|
|
551
|
+
this.reportSurfaceFailure(err.message);
|
|
552
|
+
}
|
|
553
|
+
})
|
|
554
|
+
.catch(() => { });
|
|
493
555
|
if (!textureView) {
|
|
494
556
|
return;
|
|
495
557
|
}
|
|
@@ -526,7 +588,7 @@ export class WebGPUHal extends GpuHalBase {
|
|
|
526
588
|
],
|
|
527
589
|
});
|
|
528
590
|
}
|
|
529
|
-
drawPass(passId, regionKey, bufferPassId) {
|
|
591
|
+
drawPass(passId, regionKey, bufferPassId, verticesPerInstance) {
|
|
530
592
|
if (!this.currentPass) {
|
|
531
593
|
return;
|
|
532
594
|
}
|
|
@@ -561,7 +623,7 @@ export class WebGPUHal extends GpuHalBase {
|
|
|
561
623
|
this.currentPass.setPipeline(pipeline);
|
|
562
624
|
this.currentPass.setBindGroup(0, bindGroup, [dynamicOffset]);
|
|
563
625
|
this.currentPass.setVertexBuffer(0, regionBuf.dataBuffer);
|
|
564
|
-
this.currentPass.draw(desc.verticesPerInstance, regionBuf.count);
|
|
626
|
+
this.currentPass.draw(verticesPerInstance ?? desc.verticesPerInstance, regionBuf.count);
|
|
565
627
|
}
|
|
566
628
|
/**
|
|
567
629
|
* Close the frame's state whether the submit landed or threw.
|
|
@@ -594,6 +656,7 @@ export class WebGPUHal extends GpuHalBase {
|
|
|
594
656
|
this.currentPass = null;
|
|
595
657
|
this.currentEncoder = null;
|
|
596
658
|
this.currentTextureView = null;
|
|
659
|
+
this.outgrownRings.length = 0;
|
|
597
660
|
this.drainPendingDestroy();
|
|
598
661
|
}
|
|
599
662
|
endFrame() {
|
|
@@ -606,6 +669,9 @@ export class WebGPUHal extends GpuHalBase {
|
|
|
606
669
|
if (this.currentPass) {
|
|
607
670
|
this.currentPass.end();
|
|
608
671
|
}
|
|
672
|
+
for (const ring of this.outgrownRings) {
|
|
673
|
+
this.device.queue.writeBuffer(ring.buffer, 0, this.uniformStaging, 0, ring.bytes);
|
|
674
|
+
}
|
|
609
675
|
if (slotAtSubmit > 0) {
|
|
610
676
|
const uploadSize = slotAtSubmit * this.alignedUniformSize;
|
|
611
677
|
this.device.queue.writeBuffer(this.uniformRingBuffer, 0, this.uniformStaging, 0, uploadSize);
|
|
@@ -704,8 +770,10 @@ export class WebGPUHal extends GpuHalBase {
|
|
|
704
770
|
}
|
|
705
771
|
this.regions.deleteAll();
|
|
706
772
|
this.uniformRingBuffer.destroy();
|
|
707
|
-
for (const
|
|
708
|
-
ts.
|
|
773
|
+
for (const byName of this.passTextures.values()) {
|
|
774
|
+
for (const ts of byName.values()) {
|
|
775
|
+
ts.texture.destroy();
|
|
776
|
+
}
|
|
709
777
|
}
|
|
710
778
|
this.passTextures.clear();
|
|
711
779
|
this.passBindGroups.clear();
|
package/esm/installUpload.d.ts
CHANGED
|
@@ -15,6 +15,16 @@ export interface CellTarget<K, Encoded> {
|
|
|
15
15
|
upload(key: K, data: Encoded): void;
|
|
16
16
|
release(key: K): void;
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* The other half of what this installer needs off a backend, and the half every
|
|
20
|
+
* backend gets for free by extending `GpuRenderingBackendBase` or
|
|
21
|
+
* `Canvas2DRenderingBackendBase`. Named apart from `CellTarget` because it has
|
|
22
|
+
* nothing to do with cells: the installer passes it straight through as the
|
|
23
|
+
* lifecycle's `releaseTargets` callback.
|
|
24
|
+
*/
|
|
25
|
+
export interface TargetRelease {
|
|
26
|
+
releaseOffscreenTargets(): void;
|
|
27
|
+
}
|
|
18
28
|
/**
|
|
19
29
|
* What the backend takes, read off the backend, which is the direction the
|
|
20
30
|
* overloads have to check in: the payload reaching `upload` must be assignable
|
|
@@ -74,18 +84,21 @@ export declare function oneCell<K, T>(key: K, value: T | undefined | null): Read
|
|
|
74
84
|
*
|
|
75
85
|
* **Calling again only swaps the backend.** Displays wire this from
|
|
76
86
|
* `startRenderingBackend`, which fires again on every context-loss recovery;
|
|
77
|
-
* the diff and the encode
|
|
87
|
+
* the diff and the encode memo live in the setup thunk and survive it.
|
|
88
|
+
*
|
|
89
|
+
* `encode` runs through `createEncodeMemo`. A display that needs the encoded
|
|
90
|
+
* map itself holds that memo in a `.views` closure and hands identity cells.
|
|
78
91
|
*
|
|
79
92
|
* @see installUpload.test.ts for the upload, encode and release counts.
|
|
80
93
|
* @see ADR-078 for why this is one autorun and a diff.
|
|
81
94
|
*/
|
|
82
|
-
export declare function installUpload<K, Data extends EncodedOf<B>, B extends CellTarget<K, unknown
|
|
95
|
+
export declare function installUpload<K, Data extends EncodedOf<B>, B extends CellTarget<K, unknown> & TargetRelease>(self: LifecycleHost, backend: B, spec: {
|
|
83
96
|
cells: () => ReadonlyMap<K, Data>;
|
|
84
97
|
render: (backend: B, encoded: ReadonlyMap<K, Data>) => boolean;
|
|
85
98
|
encode?: never;
|
|
86
99
|
inputs?: never;
|
|
87
100
|
}): void;
|
|
88
|
-
export declare function installUpload<K, Data, Props, B extends CellTarget<K, unknown
|
|
101
|
+
export declare function installUpload<K, Data, Props, B extends CellTarget<K, unknown> & TargetRelease>(self: LifecycleHost, backend: B, spec: UploadSpec<K, Data, Props, EncodedOf<B>, B> & {
|
|
89
102
|
encode: (data: Data, props: Props, key: K) => EncodedOf<B>;
|
|
90
103
|
}): void;
|
|
91
104
|
export {};
|