@jbrowse/render-core 5.0.0-beta.4 → 5.0.0-beta.7

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.
Files changed (69) hide show
  1. package/esm/RenderLifecycleMixin.d.ts +1 -0
  2. package/esm/RenderLifecycleMixin.js +8 -0
  3. package/esm/gpuContextLostError.d.ts +30 -0
  4. package/esm/gpuContextLostError.js +40 -0
  5. package/esm/gpuDevice.js +4 -1
  6. package/esm/hal/gpuHalBase.d.ts +3 -3
  7. package/esm/hal/index.d.ts +1 -1
  8. package/esm/hal/mockHal.d.ts +3 -3
  9. package/esm/hal/mockHal.js +3 -2
  10. package/esm/hal/oomReporter.d.ts +7 -0
  11. package/esm/hal/oomReporter.js +10 -0
  12. package/esm/hal/types.d.ts +8 -1
  13. package/esm/hal/webgl2Hal.d.ts +2 -2
  14. package/esm/hal/webgl2Hal.js +8 -1
  15. package/esm/hal/webgpuHal.d.ts +12 -2
  16. package/esm/hal/webgpuHal.js +43 -5
  17. package/esm/marks/barMark.d.ts +28 -0
  18. package/esm/marks/barMark.js +63 -0
  19. package/esm/marks/drawAgainstHit.d.ts +85 -14
  20. package/esm/marks/drawAgainstHit.js +260 -38
  21. package/esm/marks/glyphPaint.d.ts +10 -2
  22. package/esm/marks/glyphPaint.js +21 -0
  23. package/esm/marks/index.d.ts +9 -2
  24. package/esm/marks/index.js +4 -1
  25. package/esm/marks/markBackend.d.ts +14 -2
  26. package/esm/marks/markBackend.js +43 -13
  27. package/esm/marks/markHit.d.ts +8 -1
  28. package/esm/marks/markHit.js +15 -0
  29. package/esm/marks/markInk.d.ts +14 -0
  30. package/esm/marks/markInk.js +34 -0
  31. package/esm/marks/markPlan.d.ts +25 -0
  32. package/esm/marks/markPlan.js +20 -0
  33. package/esm/marks/markRamp.d.ts +43 -0
  34. package/esm/marks/markRamp.js +68 -0
  35. package/esm/marks/pointMark.d.ts +8 -4
  36. package/esm/marks/pointMark.js +32 -9
  37. package/esm/marks/spanMark.d.ts +8 -0
  38. package/esm/marks/spanMark.js +19 -14
  39. package/esm/marks/types.d.ts +90 -3
  40. package/esm/marks/types.js +56 -4
  41. package/esm/shaders/barMark.generated.d.ts +4 -0
  42. package/esm/shaders/barMark.generated.js +6 -0
  43. package/esm/shaders/barMark.iface.generated.d.ts +64 -0
  44. package/esm/shaders/barMark.iface.generated.js +110 -0
  45. package/esm/shaders/markColor.generated.d.ts +3 -0
  46. package/esm/shaders/markColor.generated.js +5 -0
  47. package/esm/shaders/pointMark.generated.d.ts +3 -3
  48. package/esm/shaders/pointMark.generated.js +3 -3
  49. package/esm/shaders/pointMark.iface.generated.d.ts +17 -6
  50. package/esm/shaders/pointMark.iface.generated.js +27 -9
  51. package/esm/shaders/pointMark.js.generated.d.ts +1 -0
  52. package/esm/shaders/pointMark.js.generated.js +39 -0
  53. package/esm/shaders/scoreScale.generated.d.ts +2 -0
  54. package/esm/shaders/scoreScale.generated.js +4 -0
  55. package/esm/useRenderingBackend.d.ts +1 -16
  56. package/esm/useRenderingBackend.js +2 -24
  57. package/package.json +4 -2
  58. package/src/shaders/barMark.generated.ts +10 -0
  59. package/src/shaders/barMark.iface.generated.ts +160 -0
  60. package/src/shaders/barMark.slang +109 -0
  61. package/src/shaders/markColor.generated.ts +8 -0
  62. package/src/shaders/markColor.slang +47 -0
  63. package/src/shaders/pointMark.generated.ts +3 -3
  64. package/src/shaders/pointMark.iface.generated.ts +34 -10
  65. package/src/shaders/pointMark.js.generated.ts +41 -0
  66. package/src/shaders/pointMark.slang +15 -26
  67. package/src/shaders/scoreScale.generated.ts +6 -0
  68. package/src/shaders/scoreScale.slang +1 -0
  69. package/src/shaders/valueScale.slang +52 -0
@@ -1,15 +1,17 @@
1
1
  // AUTO-GENERATED by packages/shader-tools/src/shader-codegen from pointMark.slang.
2
2
  // Do not edit. Run `pnpm gen:shaders` to regenerate.
3
3
 
4
- import type { VertexAttributeLayout, ShaderBinding } from '@jbrowse/render-core/hal'
4
+ import type { VertexAttributeLayout, ShaderBinding, TextureBinding } from '@jbrowse/render-core/hal'
5
5
 
6
6
  export const BINDINGS: readonly ShaderBinding[] = [
7
7
  { index: 1, kind: 'uniform', name: 'u' },
8
+ { index: 2, kind: 'texture', name: 'colorRamp' },
9
+ { index: 3, kind: 'sampler', name: 'colorRamp' },
8
10
  ]
9
11
 
10
12
  export const VERTS_PER_INSTANCE = 6
11
13
 
12
- export const UNIFORMS_SIZE_BYTES = 48
14
+ export const UNIFORMS_SIZE_BYTES = 64
13
15
 
14
16
  // Word indices into a Float32Array view over the uniform buffer.
15
17
  export const UNIFORM_OFFSET_F32 = {
@@ -17,10 +19,18 @@ export const UNIFORM_OFFSET_F32 = {
17
19
  canvasHeight: 3,
18
20
  domainMin: 4,
19
21
  domainMax: 5,
20
- zero: 6,
21
- viewportWidth: 7,
22
- radiusPx: 8,
23
- devicePixelRatio: 9,
22
+ rampMin: 8,
23
+ rampMax: 9,
24
+ zero: 10,
25
+ viewportWidth: 11,
26
+ radiusPx: 12,
27
+ devicePixelRatio: 13,
28
+ } as const
29
+
30
+ // Word indices into a Int32Array view over the uniform buffer.
31
+ export const UNIFORM_OFFSET_I32 = {
32
+ valueScaleType: 6,
33
+ rampMode: 7,
24
34
  } as const
25
35
 
26
36
 
@@ -29,6 +39,10 @@ export interface Uniforms {
29
39
  canvasHeight: number
30
40
  domainMin: number
31
41
  domainMax: number
42
+ valueScaleType: number
43
+ rampMode: number
44
+ rampMin: number
45
+ rampMax: number
32
46
  zero: number
33
47
  viewportWidth: number
34
48
  radiusPx: number
@@ -37,16 +51,21 @@ export interface Uniforms {
37
51
 
38
52
  export function writeUniforms(buf: ArrayBuffer, uniforms: Uniforms) {
39
53
  const f32 = new Float32Array(buf)
54
+ const i32 = new Int32Array(buf)
40
55
  f32[0] = uniforms.bpRangeX[0]
41
56
  f32[1] = uniforms.bpRangeX[1]
42
57
  f32[2] = uniforms.bpRangeX[2]
43
58
  f32[3] = uniforms.canvasHeight
44
59
  f32[4] = uniforms.domainMin
45
60
  f32[5] = uniforms.domainMax
46
- f32[6] = uniforms.zero
47
- f32[7] = uniforms.viewportWidth
48
- f32[8] = uniforms.radiusPx
49
- f32[9] = uniforms.devicePixelRatio
61
+ i32[6] = uniforms.valueScaleType
62
+ i32[7] = uniforms.rampMode
63
+ f32[8] = uniforms.rampMin
64
+ f32[9] = uniforms.rampMax
65
+ f32[10] = uniforms.zero
66
+ f32[11] = uniforms.viewportWidth
67
+ f32[12] = uniforms.radiusPx
68
+ f32[13] = uniforms.devicePixelRatio
50
69
  }
51
70
 
52
71
  export const INSTANCE_STRIDE_BYTES = 20
@@ -144,3 +163,8 @@ export function getInstanceGlyph(u32: Uint32Array, i: number) {
144
163
  export function setInstanceGlyph(u32: Uint32Array, i: number, v: number) {
145
164
  u32[i * INSTANCE_STRIDE_WORDS + 4] = v
146
165
  }
166
+
167
+ // Combined `Sampler2D` bindings. Texture unit indices start at 0.
168
+ export const TEXTURES: readonly [TextureBinding, ...TextureBinding[]] = [
169
+ { textureBinding: 2, samplerBinding: 3, glTextureUnit: 0, glUniformName: 'u_colorRamp', filter: 'linear' },
170
+ ]
@@ -16,10 +16,51 @@ function _min(a: number, b: number) {
16
16
  return b < a || Number.isNaN(a) ? b : a
17
17
  }
18
18
 
19
+ function symlogTransform(x: number, c: number): number {
20
+ return ((((Math.sign(x)) | 0)) * Math.log((1.0 + Math.abs((x / c)))))
21
+ }
22
+
23
+ function normalizeScore(score: number, domainMin: number, domainMax: number, scaleType: number, symlogConstant: number): number {
24
+ if ((scaleType == 2)) {
25
+ let tMin = symlogTransform(domainMin, symlogConstant)
26
+ let tRange = (symlogTransform(domainMax, symlogConstant) - tMin)
27
+ if ((tRange <= 0.0)) {
28
+ return 0.0
29
+ }
30
+ return _clamp(((symlogTransform(score, symlogConstant) - tMin) / tRange), 0.0, 1.0)
31
+ }
32
+ if ((scaleType == 1)) {
33
+ let floorV: number
34
+ if ((domainMin > 0.0)) {
35
+ floorV = domainMin
36
+ } else {
37
+ floorV = 1.0
38
+ }
39
+ let logMin = Math.log2(floorV)
40
+ let logRange = (Math.log2(_max(domainMax, floorV)) - logMin)
41
+ if ((logRange <= 0.0)) {
42
+ return 0.0
43
+ }
44
+ return _clamp(((Math.log2(_max(score, floorV)) - logMin) / logRange), 0.0, 1.0)
45
+ }
46
+ let range = (domainMax - domainMin)
47
+ if ((range <= 0.0)) {
48
+ return 0.0
49
+ }
50
+ return _clamp(((score - domainMin) / range), 0.0, 1.0)
51
+ }
52
+
19
53
  export function valueToYPx(value: number, domainMin: number, domainMax: number, h: number): number {
20
54
  return ((1.0 - _clamp(((value - domainMin) / _max((domainMax - domainMin), 9.99999997475242708e-07)), 0.0, 1.0)) * h)
21
55
  }
22
56
 
57
+ export function valueToYPxScaled(value: number, domainMin: number, domainMax: number, h: number, scaleType: number): number {
58
+ if ((scaleType == 1)) {
59
+ return ((1.0 - normalizeScore(value, domainMin, domainMax, 1, 1.0)) * h)
60
+ }
61
+ return valueToYPx(value, domainMin, domainMax, h)
62
+ }
63
+
23
64
  export function pointDrawsBar(spanPx: number, radiusPx: number): boolean {
24
65
  return (spanPx > (radiusPx * 2.0))
25
66
  }
@@ -7,12 +7,16 @@
7
7
  // All four are a 6-vertex quad; the fragment shader carves the shape.
8
8
  //! targets: wgsl, glsl
9
9
  //! export-consts: DIAMOND_GLYPH_SCALE, GLYPH_DISC, GLYPH_TRIANGLE, GLYPH_DIAMOND
10
- //! js-export: valueToYPx, pointDrawsBar
10
+ //! js-export: valueToYPx, valueToYPxScaled, pointDrawsBar
11
+ // `valueToYPx` is `valueScale`'s; it is lifted here, once, for every consumer
12
+ // of the scale — the bar shape's painter and hit test import this twin too.
11
13
 
12
14
  import antialias;
13
15
  import hpmath;
14
16
  import colorPack;
17
+ import markColor;
15
18
  import pointGlyph;
19
+ import valueScale;
16
20
 
17
21
  public static const uint VERTS_PER_INSTANCE = 6u;
18
22
 
@@ -29,6 +33,13 @@ struct Uniforms {
29
33
  float canvasHeight;
30
34
  float domainMin;
31
35
  float domainMax;
36
+ // `scoreScale`'s scale-type vocabulary, for the declared y scale.
37
+ int valueScaleType;
38
+ // `markColor`'s: 0 packs the instance's colour, 1 and 2 read its slot as a
39
+ // value through the ramp below.
40
+ int rampMode;
41
+ float rampMin;
42
+ float rampMax;
32
43
  float zero;
33
44
  float viewportWidth;
34
45
  float radiusPx;
@@ -36,34 +47,12 @@ struct Uniforms {
36
47
  float devicePixelRatio;
37
48
  };
38
49
  [[vk::binding(1, 0)]] ConstantBuffer<Uniforms> u;
50
+ [[vk::binding(2, 0)]] Sampler2D<float4> colorRamp;
39
51
 
40
52
  float bpToClipX(uint bp, Uniforms u) {
41
53
  return hpToClipX(hpSplitUint(bp), u.bpRangeX, u.zero);
42
54
  }
43
55
 
44
- // The shape's y scale: clamp into the domain, invert (a bigger value sits
45
- // higher), scale to the canvas. Linear only — a consumer that wants log
46
- // pre-transforms its values, which is what GWAS -log10 p already is.
47
- //
48
- // `//! js-export`ed because the Canvas2D painter AND the hit test run the JS
49
- // twin: a drift would move the grab target off the drawn glyph on the no-GPU
50
- // path only.
51
- //
52
- // max(range, 1e-6) guards a degenerate (min == max) domain from dividing by
53
- // zero. JS spelled that guard `|| 1`, and the two are NOT the same: `|| 1`
54
- // invents a phantom unit-wide domain, so with bounds pinned to a single value
55
- // a value half a unit above it drew half-way up the Canvas2D canvas while the
56
- // GPU clamped it to the top. Pinning a degenerate domain means everything above
57
- // it is off the top, which is what this does; the parity test pins the case.
58
- float valueToYPx(float value, float domainMin, float domainMax, float h) {
59
- float norm = clamp(
60
- (value - domainMin) / max(domainMax - domainMin, 1e-6),
61
- 0.0,
62
- 1.0,
63
- );
64
- return (1.0 - norm) * h;
65
- }
66
-
67
56
  // Whether an instance draws as an extent bar rather than a glyph: its span has
68
57
  // to beat the glyph it would otherwise be, so a datum wider than its own marker
69
58
  // stops pretending to be a point.
@@ -121,7 +110,7 @@ VsOut vs_main(PointMarkInstance inst, uint vid : SV_VertexID) {
121
110
 
122
111
  float startClipX = bpToClipX(inst.x, u);
123
112
  float endClipX = bpToClipX(inst.x2, u);
124
- float yPx = valueToYPx(inst.y, u.domainMin, u.domainMax, u.canvasHeight);
113
+ float yPx = valueToYPxScaled(inst.y, u.domainMin, u.domainMax, u.canvasHeight, u.valueScaleType);
125
114
  float centerClipY = yPxToClipY(yPx, u.canvasHeight);
126
115
 
127
116
  // Genomic span in CSS px (clip width spans -1..1 across viewportWidth).
@@ -131,7 +120,7 @@ VsOut vs_main(PointMarkInstance inst, uint vid : SV_VertexID) {
131
120
  bool isBar = pointDrawsBar(spanPx, u.radiusPx);
132
121
 
133
122
  VsOut o;
134
- o.color = unpackRGBA(inst.color);
123
+ o.color = markInstanceColor(colorRamp, inst.color, u.rampMode, u.rampMin, u.rampMax);
135
124
  if (isBar) {
136
125
  // Horizontal bar from x→x2, height 2*radiusPx px, centered on the value.
137
126
  //
@@ -0,0 +1,6 @@
1
+ // AUTO-GENERATED by packages/shader-tools/src/shader-codegen from scoreScale.slang.
2
+ // Do not edit. Run `pnpm gen:shaders` to regenerate.
3
+
4
+ export const SCALE_TYPE_LOG = 1
5
+
6
+ export const SCALE_TYPE_SYMLOG = 2
@@ -16,6 +16,7 @@
16
16
  //
17
17
  // `SCALE_TYPE_LINEAR` is deliberately absent: it is "neither of these", and a
18
18
  // third constant would be a value nothing compares against.
19
+ //! export-consts: SCALE_TYPE_LOG, SCALE_TYPE_SYMLOG
19
20
  //! js-export: normalizeScore
20
21
  //! js-skip: symlogTransform — the JS twin (wiggle-core normalize.ts) spells it Math.log1p, which is accurate near zero where the shader's log(1.0 + x) is not; a generated twin would be a faithful copy of the worse of the two. What both backends must agree on is normalizeScore, which is exported and swept by normalizeScoreParity.test.ts
21
22
  module scoreScale;
@@ -0,0 +1,52 @@
1
+ // The linear value → canvas-y scale a quantitative mark places its datum
2
+ // through: clamp into the domain, invert (a bigger value sits higher), scale to
3
+ // the canvas. `valueToYPx` is the linear scale on its own, which is what a
4
+ // consumer with no scale type of its own reads (Manhattan pins linear, and its
5
+ // -log10 p is already a pre-transform); `valueToYPxScaled` below takes a
6
+ // declared one.
7
+ //
8
+ // Two shapes compose it (ADR-095): `pointMark` reads it for a glyph centre and
9
+ // `barMark` for both cuts of a bar. Each keeps its own anchor (ADR-097) — a
10
+ // point centres on the value, a bar grows from `origin` to it — so only the
11
+ // scale lives here.
12
+ //
13
+ // max(range, 1e-6) guards a degenerate (min == max) domain from dividing by
14
+ // zero. JS spelled that guard `|| 1`, and the two are NOT the same: `|| 1`
15
+ // invents a phantom unit-wide domain, so with bounds pinned to a single value
16
+ // a value half a unit above it drew half-way up the Canvas2D canvas while the
17
+ // GPU clamped it to the top. Pinning a degenerate domain means everything above
18
+ // it is off the top, which is what this does; the parity test pins the case.
19
+ //
20
+ // A declared log scale takes the log arm of `scoreScale`'s `normalizeScore`,
21
+ // the normalizer wiggle and the coverage band are already placed by, so a mark
22
+ // display's axis ticks and its shader agree by construction. The linear arm
23
+ // stays this module's own: `normalizeScore` answers 0 on a degenerate domain
24
+ // and this answers 1, and that difference is a pinned cross-backend value
25
+ // (ADR-097).
26
+ //
27
+ // The JS twin is lifted by `pointMark.slang`'s `//! js-export` rather than
28
+ // here, the way hpmath's `expandToMinWidthPx` reaches JS through
29
+ // `coverageBar.slang`: one twin, in the generated module every consumer of the
30
+ // scale already imports.
31
+ module valueScale;
32
+
33
+ import scoreScale;
34
+
35
+ public float valueToYPx(float value, float domainMin, float domainMax, float h) {
36
+ float norm = clamp(
37
+ (value - domainMin) / max(domainMax - domainMin, 1e-6),
38
+ 0.0,
39
+ 1.0,
40
+ );
41
+ return (1.0 - norm) * h;
42
+ }
43
+
44
+ // The same placement under a declared scale type: `SCALE_TYPE_LOG` reads the
45
+ // value through `scoreScale`'s log arm, anything else through the linear one
46
+ // above.
47
+ public float valueToYPxScaled(float value, float domainMin, float domainMax, float h, int scaleType) {
48
+ if (scaleType == SCALE_TYPE_LOG) {
49
+ return (1.0 - normalizeScore(value, domainMin, domainMax, SCALE_TYPE_LOG, 1.0)) * h;
50
+ }
51
+ return valueToYPx(value, domainMin, domainMax, h);
52
+ }