@jbrowse/render-core 5.0.0-beta.7 → 5.0.0-beta.9
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 +7 -6
- package/esm/RenderLifecycleMixin.js +7 -6
- package/esm/canvas2dUtils.d.ts +58 -17
- package/esm/canvas2dUtils.js +82 -30
- package/esm/computePipeline.d.ts +4 -3
- package/esm/computePipeline.js +21 -14
- package/esm/coverageBand.d.ts +3 -3
- package/esm/coverageBand.js +2 -2
- package/esm/coverageBandBuffers.d.ts +2 -3
- package/esm/createRenderingBackend.d.ts +1 -1
- package/esm/displayPhase.d.ts +79 -109
- package/esm/displayPhase.js +42 -83
- package/esm/gpuDevice.d.ts +11 -10
- package/esm/gpuDevice.js +10 -11
- package/esm/graphicsCapabilities.d.ts +6 -7
- package/esm/graphicsCapabilities.js +3 -4
- package/esm/hal/createHal.js +1 -1
- package/esm/hal/deviceGpuCache.d.ts +25 -30
- package/esm/hal/deviceGpuCache.js +85 -97
- package/esm/hal/index.d.ts +2 -2
- package/esm/hal/index.js +1 -1
- package/esm/hal/mockHal.d.ts +8 -6
- package/esm/hal/mockHal.js +7 -2
- package/esm/hal/passIds.d.ts +8 -7
- package/esm/hal/passIds.js +11 -10
- package/esm/hal/types.d.ts +28 -12
- package/esm/hal/webgl2Hal.d.ts +11 -3
- package/esm/hal/webgl2Hal.js +116 -145
- package/esm/hal/webgpuHal.d.ts +8 -8
- package/esm/hal/webgpuHal.js +97 -124
- package/esm/instancePass.d.ts +2 -1
- package/esm/mapUploadSync.d.ts +2 -2
- package/esm/mapUploadSync.js +2 -2
- package/esm/marks/barMark.d.ts +6 -3
- package/esm/marks/barMark.js +19 -13
- package/esm/marks/drawAgainstHit.d.ts +58 -39
- package/esm/marks/drawAgainstHit.js +136 -96
- package/esm/marks/glyphPaint.d.ts +14 -2
- package/esm/marks/glyphPaint.js +16 -2
- package/esm/marks/index.d.ts +4 -2
- package/esm/marks/index.js +3 -2
- package/esm/marks/markBackend.d.ts +2 -2
- package/esm/marks/markBackend.js +2 -2
- package/esm/marks/markHit.d.ts +2 -2
- package/esm/marks/markHit.js +2 -2
- package/esm/marks/markPlan.d.ts +2 -2
- package/esm/marks/markPlan.js +2 -2
- package/esm/marks/markRamp.d.ts +1 -3
- package/esm/marks/markRamp.js +1 -5
- package/esm/marks/nearestMarkHit.d.ts +49 -0
- package/esm/marks/nearestMarkHit.js +126 -0
- package/esm/marks/pointMark.d.ts +9 -2
- package/esm/marks/pointMark.js +36 -31
- package/esm/marks/rowLane.d.ts +17 -0
- package/esm/marks/rowLane.js +11 -0
- package/esm/marks/spanMark.d.ts +6 -26
- package/esm/marks/spanMark.js +34 -31
- package/esm/marks/types.d.ts +51 -181
- package/esm/marks/types.js +91 -131
- package/esm/namedReactions.d.ts +3 -4
- package/esm/namedReactions.js +3 -4
- package/esm/scoreScale.d.ts +20 -0
- package/esm/scoreScale.js +77 -0
- package/esm/shaders/barMark.generated.d.ts +2 -3
- package/esm/shaders/barMark.generated.js +4 -3
- 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 +12 -6
- package/esm/shaders/barMark.iface.generated.js +27 -15
- package/esm/shaders/barMark.wgsl.generated.d.ts +1 -0
- package/esm/shaders/barMark.wgsl.generated.js +3 -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/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/pointMark.generated.d.ts +2 -3
- package/esm/shaders/pointMark.generated.js +4 -3
- 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 +11 -3
- package/esm/shaders/pointMark.iface.generated.js +23 -9
- package/esm/shaders/pointMark.js.generated.d.ts +1 -1
- package/esm/shaders/pointMark.js.generated.js +51 -29
- package/esm/shaders/pointMark.wgsl.generated.d.ts +1 -0
- package/esm/shaders/pointMark.wgsl.generated.js +3 -0
- package/esm/shaders/scoreScale.js.generated.js +46 -22
- package/esm/shaders/spanMark.generated.d.ts +2 -3
- package/esm/shaders/spanMark.generated.js +4 -3
- 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.js +1 -1
- package/esm/shaders/spanMark.wgsl.generated.d.ts +1 -0
- package/esm/shaders/spanMark.wgsl.generated.js +3 -0
- package/esm/slangPass.d.ts +9 -10
- package/esm/slangPass.js +9 -3
- package/esm/useGraphicsCapabilities.d.ts +1 -1
- package/esm/useGraphicsCapabilities.js +1 -1
- package/esm/webgpuUtils.d.ts +3 -11
- package/esm/webgpuUtils.js +12 -33
- package/package.json +3 -2
- package/src/shaders/barMark.generated.ts +6 -5
- package/src/shaders/barMark.glsl.generated.ts +6 -0
- package/src/shaders/barMark.iface.generated.ts +31 -15
- package/src/shaders/barMark.slang +11 -3
- package/src/shaders/barMark.wgsl.generated.ts +4 -0
- package/src/shaders/colorPack.slang +3 -2
- package/src/shaders/coverageBand.slang +5 -11
- 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 +1 -1
- package/src/shaders/coverageBar.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/hpmath.slang +18 -6
- package/src/shaders/pointMark.generated.ts +6 -6
- package/src/shaders/pointMark.glsl.generated.ts +6 -0
- package/src/shaders/pointMark.iface.generated.ts +28 -9
- package/src/shaders/pointMark.js.generated.ts +48 -29
- package/src/shaders/pointMark.slang +18 -5
- package/src/shaders/pointMark.wgsl.generated.ts +4 -0
- package/src/shaders/scoreScale.js.generated.ts +43 -22
- package/src/shaders/scoreScale.slang +37 -53
- package/src/shaders/spanMark.generated.ts +6 -5
- package/src/shaders/spanMark.glsl.generated.ts +6 -0
- package/src/shaders/spanMark.iface.generated.ts +1 -1
- package/src/shaders/spanMark.wgsl.generated.ts +4 -0
- package/src/shaders/valueScale.slang +8 -45
package/esm/RenderCanvas.d.ts
CHANGED
|
@@ -47,11 +47,11 @@ export default function RenderCanvas({ handle, drawn, phase, ...canvasProps }: {
|
|
|
47
47
|
/**
|
|
48
48
|
* Whether this canvas holds finished content — each view's own `settled`, the
|
|
49
49
|
* same flag its `*_canvas_done` testid is built from. Published as
|
|
50
|
-
* `data-display-drawn`,
|
|
50
|
+
* `data-display-drawn`, the attribute `PENDING_DISPLAYS`
|
|
51
51
|
* (`@jbrowse/browser-test-utils`) selects on, so these views answer "has
|
|
52
52
|
* everything painted?" with the same attribute every LGV display does.
|
|
53
53
|
*
|
|
54
|
-
*
|
|
54
|
+
* The prop is required because `PENDING_DISPLAYS` used to name
|
|
55
55
|
* `synteny_canvas` explicitly and simply **forgot dotplot**, so an unpainted
|
|
56
56
|
* dotplot counted as finished and a capture could land on it blank. A new
|
|
57
57
|
* drop-to-primitive view would have been forgotten the same way.
|
|
@@ -142,8 +142,8 @@ export declare function RenderLifecycleMixin(): import("@jbrowse/mobx-state-tree
|
|
|
142
142
|
* #getter
|
|
143
143
|
* **The first-paint answer every consumer outside the display should
|
|
144
144
|
* read**, `canvasDrawn` being only the raw flag: a display that is
|
|
145
|
-
* deliberately not painting a canvas has finished, and
|
|
146
|
-
*
|
|
145
|
+
* deliberately not painting a canvas has finished, and reporting it
|
|
146
|
+
* unfinished leaves every waiter on it waiting forever.
|
|
147
147
|
*
|
|
148
148
|
* The two `rendersCanvas: false` states each had three of their four
|
|
149
149
|
* consumers wired by hand — the loading scrim (`rendersCanvas` /
|
|
@@ -151,10 +151,11 @@ export declare function RenderLifecycleMixin(): import("@jbrowse/mobx-state-tree
|
|
|
151
151
|
* while the fourth, `data-display-drawn`, went on publishing `"false"`
|
|
152
152
|
* forever off the raw flag. That attribute is what `PENDING_DISPLAYS`
|
|
153
153
|
* (`@jbrowse/browser-test-utils`) selects on, so a zoomed-out reference
|
|
154
|
-
* sequence track made every `waitForDisplaysDone` on the page
|
|
155
|
-
* full timeout
|
|
156
|
-
*
|
|
157
|
-
* one outside the display, so the display has to
|
|
154
|
+
* sequence track made every `waitForDisplaysDone` on the page run to its
|
|
155
|
+
* full timeout, and that wait swallows its own timeout without reporting
|
|
156
|
+
* it. `fetchInert` on the comparative side has the same problem: the
|
|
157
|
+
* forgotten reader is the one outside the display, so the display has to
|
|
158
|
+
* publish one name for it.
|
|
158
159
|
*
|
|
159
160
|
* `paintInert` is the third term and the same argument once more, for the
|
|
160
161
|
* state where a display *would* paint a canvas and never gets to — a fetch
|
|
@@ -164,8 +164,8 @@ export function RenderLifecycleMixin() {
|
|
|
164
164
|
* #getter
|
|
165
165
|
* **The first-paint answer every consumer outside the display should
|
|
166
166
|
* read**, `canvasDrawn` being only the raw flag: a display that is
|
|
167
|
-
* deliberately not painting a canvas has finished, and
|
|
168
|
-
*
|
|
167
|
+
* deliberately not painting a canvas has finished, and reporting it
|
|
168
|
+
* unfinished leaves every waiter on it waiting forever.
|
|
169
169
|
*
|
|
170
170
|
* The two `rendersCanvas: false` states each had three of their four
|
|
171
171
|
* consumers wired by hand — the loading scrim (`rendersCanvas` /
|
|
@@ -173,10 +173,11 @@ export function RenderLifecycleMixin() {
|
|
|
173
173
|
* while the fourth, `data-display-drawn`, went on publishing `"false"`
|
|
174
174
|
* forever off the raw flag. That attribute is what `PENDING_DISPLAYS`
|
|
175
175
|
* (`@jbrowse/browser-test-utils`) selects on, so a zoomed-out reference
|
|
176
|
-
* sequence track made every `waitForDisplaysDone` on the page
|
|
177
|
-
* full timeout
|
|
178
|
-
*
|
|
179
|
-
* one outside the display, so the display has to
|
|
176
|
+
* sequence track made every `waitForDisplaysDone` on the page run to its
|
|
177
|
+
* full timeout, and that wait swallows its own timeout without reporting
|
|
178
|
+
* it. `fetchInert` on the comparative side has the same problem: the
|
|
179
|
+
* forgotten reader is the one outside the display, so the display has to
|
|
180
|
+
* publish one name for it.
|
|
180
181
|
*
|
|
181
182
|
* `paintInert` is the third term and the same argument once more, for the
|
|
182
183
|
* state where a display *would* paint a canvas and never gets to — a fetch
|
package/esm/canvas2dUtils.d.ts
CHANGED
|
@@ -109,7 +109,7 @@ export interface ClipContext2D {
|
|
|
109
109
|
* Clip to one rect, paint, unclip — the `save`/`clip`/`restore` triple with the
|
|
110
110
|
* `restore` in a `finally`.
|
|
111
111
|
*
|
|
112
|
-
* The `finally` is
|
|
112
|
+
* The `finally` is required, and missing it breaks more than the one frame: an
|
|
113
113
|
* on-screen ctx outlives the frame and `prepareCanvas`'s setTransform/clearRect
|
|
114
114
|
* do **not** reset clip state, so one painter throwing once leaves every later
|
|
115
115
|
* frame drawing through a stale clip. A transient error becomes permanent
|
|
@@ -120,7 +120,7 @@ export interface ClipContext2D {
|
|
|
120
120
|
* own. Reach for this one for the clips *nested inside* a block — a display
|
|
121
121
|
* whose bands each own a horizontal strip of the canvas (alignments clips its
|
|
122
122
|
* coverage, pileup and arc bands separately within one block clip), where the
|
|
123
|
-
* nesting
|
|
123
|
+
* nesting makes a hand-rolled pairing easy to get wrong: an early
|
|
124
124
|
* `return` inside the band body unbalances the block's `save` too.
|
|
125
125
|
*/
|
|
126
126
|
export declare function withClip(ctx: ClipContext2D, x: number, y: number, w: number, h: number, paint: () => void): void;
|
|
@@ -229,8 +229,31 @@ export declare function spanRect(toX: (bp: number) => number, startBp: number, e
|
|
|
229
229
|
width: number;
|
|
230
230
|
};
|
|
231
231
|
export declare function makeRampFillStyleLut(ramp: Uint8Array): (t: number) => string;
|
|
232
|
-
|
|
232
|
+
/**
|
|
233
|
+
* A block's bp→px projection as four numbers, for a placement function reading
|
|
234
|
+
* its block off a frame object. `makeBpMapper` closes over the same four.
|
|
235
|
+
*/
|
|
236
|
+
export interface BpProjection {
|
|
237
|
+
originPx: number;
|
|
238
|
+
startBp: number;
|
|
239
|
+
spanBp: number;
|
|
240
|
+
signedSpanPx: number;
|
|
241
|
+
}
|
|
242
|
+
export declare function bpProjection(bounds: BpRegionBounds): BpProjection;
|
|
243
|
+
export declare function projectBp(p: BpProjection, bp: number): number;
|
|
244
|
+
/**
|
|
245
|
+
* One closure literal for both orientations. Split per orientation, a paint
|
|
246
|
+
* loop's mapper call goes megamorphic on a view mixing them, and the loop ran
|
|
247
|
+
* 1.9-2.3x slower (`benches/bpMapper.bench.ts`).
|
|
248
|
+
*/
|
|
233
249
|
export declare function makeBpMapper(bounds: BpRegionBounds): (bp: number) => number;
|
|
250
|
+
/**
|
|
251
|
+
* Screen px one base occupies in this region: the scale factor, unsigned, with
|
|
252
|
+
* the block's direction left to `makeBpMapper` and `spanLeft`. Shared so a mark
|
|
253
|
+
* sized against it in a draw pass and hit-tested against it in a component
|
|
254
|
+
* read one number.
|
|
255
|
+
*/
|
|
256
|
+
export declare function pxPerBpOf(bounds: BpRegionBounds): number;
|
|
234
257
|
/**
|
|
235
258
|
* Left edge of the **1bp cell** covering `bp` — for painters that fill a rect
|
|
236
259
|
* per base (MAF alignment cells, the alignments pileup's mismatch /
|
|
@@ -255,18 +278,6 @@ export declare function makeBpMapper(bounds: BpRegionBounds): (bp: number) => nu
|
|
|
255
278
|
* bp-scale; the pileup floors at 1px and adds a seam fudge). Only the pivot is
|
|
256
279
|
* shared.
|
|
257
280
|
*/
|
|
258
|
-
/**
|
|
259
|
-
* Screen px one base occupies in this region — the scale factor, unsigned, with
|
|
260
|
-
* the block's direction deliberately left out (that lives in `makeBpMapper` and
|
|
261
|
-
* `spanLeft`, and folding it in here would make a *scale* carry an orientation).
|
|
262
|
-
*
|
|
263
|
-
* Shared because it is usually an input to something a second painter has to
|
|
264
|
-
* reproduce exactly: a mark sized against it in a draw pass and hit-tested
|
|
265
|
-
* against it in a component is two spellings of one number, and the hit target
|
|
266
|
-
* silently stops covering the mark if they ever drift. Trivial arithmetic, but
|
|
267
|
-
* the drift is not.
|
|
268
|
-
*/
|
|
269
|
-
export declare function pxPerBpOf(bounds: BpRegionBounds): number;
|
|
270
281
|
export declare function makeCellLeftMapper(bounds: BpRegionBounds): (bp: number) => number;
|
|
271
282
|
/**
|
|
272
283
|
* Fill the rect covering genomic `[startBp, endBp)`, given a bare
|
|
@@ -276,7 +287,7 @@ export declare function makeCellLeftMapper(bounds: BpRegionBounds): (bp: number)
|
|
|
276
287
|
*
|
|
277
288
|
* Prefer this to `makeCellLeftMapper` + a caller-computed width for anything
|
|
278
289
|
* wider than one base. The pivot in `makeCellLeftMapper` is exactly one base,
|
|
279
|
-
* which is right for a 1bp cell and
|
|
290
|
+
* which is right for a 1bp cell and wrong for an N-bp span — it
|
|
280
291
|
* anchors the rect one base from the correct edge and then grows it the wrong
|
|
281
292
|
* way. `fillBpSpan(bp, bp + 1)` is *identical* to the cell-mapper spelling, so
|
|
282
293
|
* a per-base painter can use this too and never think about orientation again.
|
|
@@ -333,7 +344,7 @@ export declare function fillBpSpan(ctx: {
|
|
|
333
344
|
* `14468.9936` — and rounding the *offset* and then adding such an anchor
|
|
334
345
|
* returns the anchor's fraction untouched. The result is then not a base at
|
|
335
346
|
* all, and every caller that indexes one by equality (a mismatch column, a
|
|
336
|
-
* coverage bin, the sort-at-this-column anchor)
|
|
347
|
+
* coverage bin, the sort-at-this-column anchor) finds no match: the pileup
|
|
337
348
|
* offered no SNP submenu over a mismatch it was drawing. Rounding
|
|
338
349
|
* `whole + fraction + offset` in one step instead fixes that and hands back the
|
|
339
350
|
* genome-scale addend the paragraph above exists to keep out, so the whole base
|
|
@@ -395,3 +406,33 @@ export declare function regionAtPixel<R extends {
|
|
|
395
406
|
screenStartPx: number;
|
|
396
407
|
screenEndPx: number;
|
|
397
408
|
}>(regions: readonly R[], px: number): R | undefined;
|
|
409
|
+
/**
|
|
410
|
+
* CSS px a Canvas2D painter adds to the drawn width of a rect whose neighbours
|
|
411
|
+
* abut it. `fillRect` antialiases each edge on its own, so two rects meeting on
|
|
412
|
+
* a fractional column each take partial coverage of it and a hairline of
|
|
413
|
+
* background shows through; abutting GPU quads share an exact clip-space edge
|
|
414
|
+
* and the rasterizer fills it once, so a shader grows no matching pad.
|
|
415
|
+
*/
|
|
416
|
+
export declare const CANVAS_SEAM_PX = 0.8;
|
|
417
|
+
export interface PathPaintContext {
|
|
418
|
+
beginPath(): void;
|
|
419
|
+
fill(): void;
|
|
420
|
+
stroke(): void;
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* One batched Canvas2D path of many shapes, painted in pieces a browser will
|
|
424
|
+
* rasterize. Opens a path on construction; call `add()` before appending each
|
|
425
|
+
* shape and `flush()` to paint what it holds and reopen.
|
|
426
|
+
*/
|
|
427
|
+
export declare class CappedPath {
|
|
428
|
+
private shapes;
|
|
429
|
+
private readonly ctx;
|
|
430
|
+
private readonly paint;
|
|
431
|
+
constructor(ctx: PathPaintContext, paint: 'fill' | 'stroke');
|
|
432
|
+
/**
|
|
433
|
+
* True when the path was full and has been painted and reopened, so a
|
|
434
|
+
* polyline must `moveTo` its pen again.
|
|
435
|
+
*/
|
|
436
|
+
add(): boolean;
|
|
437
|
+
flush(): void;
|
|
438
|
+
}
|
package/esm/canvas2dUtils.js
CHANGED
|
@@ -216,7 +216,7 @@ export function clipBlockForCanvas(block, canvasWidth) {
|
|
|
216
216
|
* Clip to one rect, paint, unclip — the `save`/`clip`/`restore` triple with the
|
|
217
217
|
* `restore` in a `finally`.
|
|
218
218
|
*
|
|
219
|
-
* The `finally` is
|
|
219
|
+
* The `finally` is required, and missing it breaks more than the one frame: an
|
|
220
220
|
* on-screen ctx outlives the frame and `prepareCanvas`'s setTransform/clearRect
|
|
221
221
|
* do **not** reset clip state, so one painter throwing once leaves every later
|
|
222
222
|
* frame drawing through a stale clip. A transient error becomes permanent
|
|
@@ -227,7 +227,7 @@ export function clipBlockForCanvas(block, canvasWidth) {
|
|
|
227
227
|
* own. Reach for this one for the clips *nested inside* a block — a display
|
|
228
228
|
* whose bands each own a horizontal strip of the canvas (alignments clips its
|
|
229
229
|
* coverage, pileup and arc bands separately within one block clip), where the
|
|
230
|
-
* nesting
|
|
230
|
+
* nesting makes a hand-rolled pairing easy to get wrong: an early
|
|
231
231
|
* `return` inside the band body unbalances the block's `save` too.
|
|
232
232
|
*/
|
|
233
233
|
export function withClip(ctx, x, y, w, h, paint) {
|
|
@@ -386,21 +386,36 @@ export function makeRampFillStyleLut(ramp) {
|
|
|
386
386
|
return s;
|
|
387
387
|
};
|
|
388
388
|
}
|
|
389
|
-
export function
|
|
390
|
-
const
|
|
389
|
+
export function bpProjection(bounds) {
|
|
390
|
+
const { start, end, screenStartPx, screenEndPx, reversed } = bounds;
|
|
391
391
|
const w = screenEndPx - screenStartPx;
|
|
392
|
-
return
|
|
392
|
+
return {
|
|
393
|
+
originPx: reversed ? screenEndPx : screenStartPx,
|
|
394
|
+
startBp: start,
|
|
395
|
+
spanBp: end - start,
|
|
396
|
+
signedSpanPx: reversed ? -w : w,
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
export function projectBp(p, bp) {
|
|
400
|
+
return p.originPx + ((bp - p.startBp) / p.spanBp) * p.signedSpanPx;
|
|
393
401
|
}
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
402
|
+
/**
|
|
403
|
+
* One closure literal for both orientations. Split per orientation, a paint
|
|
404
|
+
* loop's mapper call goes megamorphic on a view mixing them, and the loop ran
|
|
405
|
+
* 1.9-2.3x slower (`benches/bpMapper.bench.ts`).
|
|
406
|
+
*/
|
|
397
407
|
export function makeBpMapper(bounds) {
|
|
398
|
-
const {
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
408
|
+
const { originPx, startBp, spanBp, signedSpanPx } = bpProjection(bounds);
|
|
409
|
+
return (bp) => originPx + ((bp - startBp) / spanBp) * signedSpanPx;
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Screen px one base occupies in this region: the scale factor, unsigned, with
|
|
413
|
+
* the block's direction left to `makeBpMapper` and `spanLeft`. Shared so a mark
|
|
414
|
+
* sized against it in a draw pass and hit-tested against it in a component
|
|
415
|
+
* read one number.
|
|
416
|
+
*/
|
|
417
|
+
export function pxPerBpOf(bounds) {
|
|
418
|
+
return ((bounds.screenEndPx - bounds.screenStartPx) / (bounds.end - bounds.start));
|
|
404
419
|
}
|
|
405
420
|
/**
|
|
406
421
|
* Left edge of the **1bp cell** covering `bp` — for painters that fill a rect
|
|
@@ -426,20 +441,6 @@ export function makeBpMapper(bounds) {
|
|
|
426
441
|
* bp-scale; the pileup floors at 1px and adds a seam fudge). Only the pivot is
|
|
427
442
|
* shared.
|
|
428
443
|
*/
|
|
429
|
-
/**
|
|
430
|
-
* Screen px one base occupies in this region — the scale factor, unsigned, with
|
|
431
|
-
* the block's direction deliberately left out (that lives in `makeBpMapper` and
|
|
432
|
-
* `spanLeft`, and folding it in here would make a *scale* carry an orientation).
|
|
433
|
-
*
|
|
434
|
-
* Shared because it is usually an input to something a second painter has to
|
|
435
|
-
* reproduce exactly: a mark sized against it in a draw pass and hit-tested
|
|
436
|
-
* against it in a component is two spellings of one number, and the hit target
|
|
437
|
-
* silently stops covering the mark if they ever drift. Trivial arithmetic, but
|
|
438
|
-
* the drift is not.
|
|
439
|
-
*/
|
|
440
|
-
export function pxPerBpOf(bounds) {
|
|
441
|
-
return ((bounds.screenEndPx - bounds.screenStartPx) / (bounds.end - bounds.start));
|
|
442
|
-
}
|
|
443
444
|
export function makeCellLeftMapper(bounds) {
|
|
444
445
|
const toX = makeBpMapper(bounds);
|
|
445
446
|
const pxPerBp = pxPerBpOf(bounds);
|
|
@@ -456,7 +457,7 @@ export function makeCellLeftMapper(bounds) {
|
|
|
456
457
|
*
|
|
457
458
|
* Prefer this to `makeCellLeftMapper` + a caller-computed width for anything
|
|
458
459
|
* wider than one base. The pivot in `makeCellLeftMapper` is exactly one base,
|
|
459
|
-
* which is right for a 1bp cell and
|
|
460
|
+
* which is right for a 1bp cell and wrong for an N-bp span — it
|
|
460
461
|
* anchors the rect one base from the correct edge and then grows it the wrong
|
|
461
462
|
* way. `fillBpSpan(bp, bp + 1)` is *identical* to the cell-mapper spelling, so
|
|
462
463
|
* a per-base painter can use this too and never think about orientation again.
|
|
@@ -515,7 +516,7 @@ export function fillBpSpan(ctx, toX, startBp, endBp, top, height, seam = 0) {
|
|
|
515
516
|
* `14468.9936` — and rounding the *offset* and then adding such an anchor
|
|
516
517
|
* returns the anchor's fraction untouched. The result is then not a base at
|
|
517
518
|
* all, and every caller that indexes one by equality (a mismatch column, a
|
|
518
|
-
* coverage bin, the sort-at-this-column anchor)
|
|
519
|
+
* coverage bin, the sort-at-this-column anchor) finds no match: the pileup
|
|
519
520
|
* offered no SNP submenu over a mismatch it was drawing. Rounding
|
|
520
521
|
* `whole + fraction + offset` in one step instead fixes that and hands back the
|
|
521
522
|
* genome-scale addend the paragraph above exists to keep out, so the whole base
|
|
@@ -589,3 +590,54 @@ export function bpAtPxExact(px, bounds) {
|
|
|
589
590
|
export function regionAtPixel(regions, px) {
|
|
590
591
|
return regions.find(r => px >= r.screenStartPx && px < r.screenEndPx);
|
|
591
592
|
}
|
|
593
|
+
/**
|
|
594
|
+
* CSS px a Canvas2D painter adds to the drawn width of a rect whose neighbours
|
|
595
|
+
* abut it. `fillRect` antialiases each edge on its own, so two rects meeting on
|
|
596
|
+
* a fractional column each take partial coverage of it and a hairline of
|
|
597
|
+
* background shows through; abutting GPU quads share an exact clip-space edge
|
|
598
|
+
* and the rasterizer fills it once, so a shader grows no matching pad.
|
|
599
|
+
*/
|
|
600
|
+
export const CANVAS_SEAM_PX = 0.8;
|
|
601
|
+
/**
|
|
602
|
+
* Shapes a `CappedPath` holds before it paints and reopens. Chrome's
|
|
603
|
+
* GPU-rasterized canvas, which is desktop Chrome with a GPU and headless Chrome
|
|
604
|
+
* with the GPU blocklist off, silently paints nothing for a path past about
|
|
605
|
+
* 16 MiB: 180,000 discs, 453,000 rects or 931,000 line segments.
|
|
606
|
+
* `pnpm check-canvas-path-limits` drives the painters past it in a real browser.
|
|
607
|
+
*/
|
|
608
|
+
const MAX_SHAPES_PER_PATH = 10_000;
|
|
609
|
+
/**
|
|
610
|
+
* One batched Canvas2D path of many shapes, painted in pieces a browser will
|
|
611
|
+
* rasterize. Opens a path on construction; call `add()` before appending each
|
|
612
|
+
* shape and `flush()` to paint what it holds and reopen.
|
|
613
|
+
*/
|
|
614
|
+
export class CappedPath {
|
|
615
|
+
shapes = 0;
|
|
616
|
+
ctx;
|
|
617
|
+
paint;
|
|
618
|
+
constructor(ctx, paint) {
|
|
619
|
+
this.ctx = ctx;
|
|
620
|
+
this.paint = paint;
|
|
621
|
+
ctx.beginPath();
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* True when the path was full and has been painted and reopened, so a
|
|
625
|
+
* polyline must `moveTo` its pen again.
|
|
626
|
+
*/
|
|
627
|
+
add() {
|
|
628
|
+
if (this.shapes === MAX_SHAPES_PER_PATH) {
|
|
629
|
+
this.flush();
|
|
630
|
+
this.shapes = 1;
|
|
631
|
+
return true;
|
|
632
|
+
}
|
|
633
|
+
this.shapes++;
|
|
634
|
+
return false;
|
|
635
|
+
}
|
|
636
|
+
flush() {
|
|
637
|
+
if (this.shapes > 0) {
|
|
638
|
+
this.ctx[this.paint]();
|
|
639
|
+
this.ctx.beginPath();
|
|
640
|
+
this.shapes = 0;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
}
|
package/esm/computePipeline.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ShaderBinding } from './hal/index.ts';
|
|
1
|
+
import type { ShaderBinding, ShaderSource } from './hal/index.ts';
|
|
2
2
|
export interface ComputePipelineState {
|
|
3
3
|
device: GPUDevice;
|
|
4
4
|
pipeline: GPUComputePipeline;
|
|
@@ -8,6 +8,7 @@ export interface ComputePipelineState {
|
|
|
8
8
|
/**
|
|
9
9
|
* One compute pipeline per kernel, built on first use against the current
|
|
10
10
|
* device and rebuilt after a device loss. Concurrent first callers share the
|
|
11
|
-
* one async build
|
|
11
|
+
* one async build, which is also when the kernel's WGSL is loaded; a failed
|
|
12
|
+
* one is retried by the next caller rather than kept.
|
|
12
13
|
*/
|
|
13
|
-
export declare function makeComputePipelineCache(
|
|
14
|
+
export declare function makeComputePipelineCache(source: Pick<ShaderSource, 'wgsl'>, entryPoint: string, bindings: readonly ShaderBinding[]): (device: GPUDevice) => Promise<ComputePipelineState>;
|
package/esm/computePipeline.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/// <reference types="@webgpu/types" />
|
|
2
2
|
import { onDeviceLost } from './gpuDevice.js';
|
|
3
|
+
import { stageVisibility } from './webgpuUtils.js';
|
|
3
4
|
// Built from the kernel's own reflected binding table rather than restated by
|
|
4
5
|
// the caller, so a mismatch shows up as a validation failure at pipeline
|
|
5
6
|
// creation rather than as a wrong buffer under a right-looking index.
|
|
@@ -12,7 +13,7 @@ function makeBindGroupLayout(device, bindings) {
|
|
|
12
13
|
}
|
|
13
14
|
return {
|
|
14
15
|
binding: b.index,
|
|
15
|
-
visibility:
|
|
16
|
+
visibility: stageVisibility(b.stages),
|
|
16
17
|
buffer: { type: b.kind },
|
|
17
18
|
};
|
|
18
19
|
}),
|
|
@@ -21,9 +22,10 @@ function makeBindGroupLayout(device, bindings) {
|
|
|
21
22
|
/**
|
|
22
23
|
* One compute pipeline per kernel, built on first use against the current
|
|
23
24
|
* device and rebuilt after a device loss. Concurrent first callers share the
|
|
24
|
-
* one async build
|
|
25
|
+
* one async build, which is also when the kernel's WGSL is loaded; a failed
|
|
26
|
+
* one is retried by the next caller rather than kept.
|
|
25
27
|
*/
|
|
26
|
-
export function makeComputePipelineCache(
|
|
28
|
+
export function makeComputePipelineCache(source, entryPoint, bindings) {
|
|
27
29
|
let state = null;
|
|
28
30
|
let statePromise = null;
|
|
29
31
|
onDeviceLost(() => {
|
|
@@ -38,17 +40,22 @@ export function makeComputePipelineCache(code, entryPoint, bindings) {
|
|
|
38
40
|
return statePromise;
|
|
39
41
|
}
|
|
40
42
|
statePromise = (async () => {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
43
|
+
try {
|
|
44
|
+
const { WGSL_SOURCE } = await source.wgsl();
|
|
45
|
+
const module = device.createShaderModule({ code: WGSL_SOURCE });
|
|
46
|
+
const bindGroupLayout = makeBindGroupLayout(device, bindings);
|
|
47
|
+
const pipeline = await device.createComputePipelineAsync({
|
|
48
|
+
layout: device.createPipelineLayout({
|
|
49
|
+
bindGroupLayouts: [bindGroupLayout],
|
|
50
|
+
}),
|
|
51
|
+
compute: { module, entryPoint },
|
|
52
|
+
});
|
|
53
|
+
state = { device, pipeline, bindGroupLayout, bindings };
|
|
54
|
+
return state;
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
statePromise = null;
|
|
58
|
+
}
|
|
52
59
|
})();
|
|
53
60
|
return statePromise;
|
|
54
61
|
};
|
package/esm/coverageBand.d.ts
CHANGED
|
@@ -80,7 +80,7 @@ export interface CoverageBandUniformValues {
|
|
|
80
80
|
covHeight: number;
|
|
81
81
|
covYOffset: number;
|
|
82
82
|
covTop: number;
|
|
83
|
-
/** The region's
|
|
83
|
+
/** The region's peak depth; the buffers' `relDepth` is a fraction of it. */
|
|
84
84
|
regionMaxDepth: number;
|
|
85
85
|
/** The display's autoscaled depth domain. `undefined` max = not resolved yet. */
|
|
86
86
|
domainMin: number;
|
|
@@ -98,8 +98,8 @@ export interface CoverageBandUniformValues {
|
|
|
98
98
|
* Fill the coverage band's uniform buffer. Total-write (the generated packer),
|
|
99
99
|
* so a field left out is a compile error rather than last frame's value.
|
|
100
100
|
*
|
|
101
|
-
* `hpZero` is derived here rather than at each call site because
|
|
102
|
-
*
|
|
101
|
+
* `hpZero` is derived here rather than at each call site because a wrong value
|
|
102
|
+
* draws a plausible but wrong band and raises no error: it MUST be 0, since the HP
|
|
103
103
|
* math materializes +inf as `1/hpZero` to stop the compiler folding the hi/lo
|
|
104
104
|
* split it exists to preserve.
|
|
105
105
|
*/
|
package/esm/coverageBand.js
CHANGED
|
@@ -69,8 +69,8 @@ export const COVERAGE_INDICATOR_PASS = {
|
|
|
69
69
|
* Fill the coverage band's uniform buffer. Total-write (the generated packer),
|
|
70
70
|
* so a field left out is a compile error rather than last frame's value.
|
|
71
71
|
*
|
|
72
|
-
* `hpZero` is derived here rather than at each call site because
|
|
73
|
-
*
|
|
72
|
+
* `hpZero` is derived here rather than at each call site because a wrong value
|
|
73
|
+
* draws a plausible but wrong band and raises no error: it MUST be 0, since the HP
|
|
74
74
|
* math materializes +inf as `1/hpZero` to stop the compiler folding the hi/lo
|
|
75
75
|
* split it exists to preserve.
|
|
76
76
|
*/
|
|
@@ -7,9 +7,8 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Structural rather than nominal, and named after the layout rather than after
|
|
9
9
|
* either display: the alignments worker's `CoverageUploadData` and the MAF
|
|
10
|
-
* worker's `MafCoverageRegion` both satisfy it,
|
|
11
|
-
*
|
|
12
|
-
* restating four field reads.
|
|
10
|
+
* worker's `MafCoverageRegion` both satisfy it, so the passes in
|
|
11
|
+
* `coverageBand.ts` carry the packers and no plugin restates four field reads.
|
|
13
12
|
*
|
|
14
13
|
* Its own module, apart from the GPU passes in `coverageBand.ts`: a state
|
|
15
14
|
* model reaching for just this shape has no reason to statically pull in
|
|
@@ -19,7 +19,7 @@ export interface RenderingBackendOptions<TRenderingBackend> {
|
|
|
19
19
|
* panel hold 109.7 MiB nothing counts (ARCHITECTURAL_LIMITS.md §"The MSAA
|
|
20
20
|
* target is the largest per-display allocation"); 1 allocates none. Whether a
|
|
21
21
|
* display's pixels survive the flip is a look-at-the-pixels decision taken
|
|
22
|
-
* one display at a time; `ideas/arc-antialiasing-without-msaa.md` is the
|
|
22
|
+
* one display at a time; `ideas/ready/arc-antialiasing-without-msaa.md` is the
|
|
23
23
|
* survey and the captures.
|
|
24
24
|
*/
|
|
25
25
|
sampleCount?: SampleCount;
|