@jbrowse/render-core 5.0.0-beta.1 → 5.0.0-beta.3

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 (120) hide show
  1. package/esm/RenderLifecycleMixin.d.ts +25 -6
  2. package/esm/RenderLifecycleMixin.js +30 -7
  3. package/esm/canvas2dUtils.d.ts +25 -0
  4. package/esm/canvas2dUtils.js +27 -0
  5. package/esm/colorRampLut.d.ts +4 -4
  6. package/esm/colorRampLut.js +4 -4
  7. package/esm/computePipeline.d.ts +13 -0
  8. package/esm/computePipeline.js +55 -0
  9. package/esm/contractReports.d.ts +54 -0
  10. package/esm/contractReports.js +112 -0
  11. package/esm/coverageBand.d.ts +0 -1
  12. package/esm/coverageBand.js +0 -1
  13. package/esm/coverageBandBuffers.d.ts +0 -6
  14. package/esm/coverageBandBuffers.js +1 -13
  15. package/esm/createRenderingBackend.d.ts +14 -18
  16. package/esm/createRenderingBackend.js +4 -2
  17. package/esm/displayPhase.d.ts +10 -1
  18. package/esm/displayPhase.js +2 -1
  19. package/esm/encodeMemo.d.ts +14 -0
  20. package/esm/encodeMemo.js +48 -0
  21. package/esm/gpuDevice.d.ts +9 -0
  22. package/esm/gpuDevice.js +12 -3
  23. package/esm/graphicsCapabilities.d.ts +18 -15
  24. package/esm/graphicsCapabilities.js +23 -16
  25. package/esm/hal/createHal.d.ts +1 -2
  26. package/esm/hal/createHal.js +14 -4
  27. package/esm/hal/deviceGpuCache.js +2 -2
  28. package/esm/hal/gpuHalBase.d.ts +1 -1
  29. package/esm/hal/gpuHalBase.js +2 -3
  30. package/esm/hal/mockHal.d.ts +0 -1
  31. package/esm/hal/mockHal.js +0 -10
  32. package/esm/hal/regionRegistry.d.ts +0 -1
  33. package/esm/hal/regionRegistry.js +3 -15
  34. package/esm/hal/types.d.ts +5 -1
  35. package/esm/hal/webgl2Hal.d.ts +1 -1
  36. package/esm/hal/webgl2Hal.js +2 -2
  37. package/esm/hal/webgpuHal.d.ts +2 -2
  38. package/esm/hal/webgpuHal.js +13 -10
  39. package/esm/installUpload.d.ts +4 -1
  40. package/esm/installUpload.js +10 -41
  41. package/esm/instanceCache.d.ts +10 -7
  42. package/esm/instanceCache.js +14 -15
  43. package/esm/mapUploadSync.d.ts +0 -8
  44. package/esm/mapUploadSync.js +0 -1
  45. package/esm/marks/colorFill.d.ts +17 -0
  46. package/esm/marks/colorFill.js +26 -0
  47. package/esm/marks/drawAgainstHit.d.ts +47 -0
  48. package/esm/marks/drawAgainstHit.js +140 -0
  49. package/esm/marks/glyphPaint.d.ts +16 -0
  50. package/esm/marks/glyphPaint.js +41 -0
  51. package/esm/marks/index.d.ts +9 -0
  52. package/esm/marks/index.js +6 -0
  53. package/esm/marks/markBackend.d.ts +87 -0
  54. package/esm/marks/markBackend.js +133 -0
  55. package/esm/marks/markPaint.d.ts +13 -0
  56. package/esm/marks/markPaint.js +18 -0
  57. package/esm/marks/pointMark.d.ts +24 -0
  58. package/esm/marks/pointMark.js +98 -0
  59. package/esm/marks/spanMark.d.ts +45 -0
  60. package/esm/marks/spanMark.js +71 -0
  61. package/esm/marks/types.d.ts +206 -0
  62. package/esm/marks/types.js +125 -0
  63. package/esm/perRegionRenderingBackend.d.ts +15 -3
  64. package/esm/perRegionRenderingBackend.js +26 -3
  65. package/esm/regionDataMap.d.ts +2 -2
  66. package/esm/regionDataMap.js +25 -23
  67. package/esm/renderingBackendBase.d.ts +11 -14
  68. package/esm/renderingBackendBase.js +7 -7
  69. package/esm/shaders/capsule.js.generated.d.ts +1 -0
  70. package/esm/shaders/capsule.js.generated.js +11 -0
  71. package/esm/shaders/coverageBar.generated.d.ts +2 -2
  72. package/esm/shaders/coverageBar.generated.js +2 -2
  73. package/esm/shaders/coverageMod.generated.d.ts +2 -2
  74. package/esm/shaders/coverageMod.generated.js +2 -2
  75. package/esm/shaders/coverageSnp.generated.d.ts +2 -2
  76. package/esm/shaders/coverageSnp.generated.js +2 -2
  77. package/esm/shaders/hpmath.js.generated.d.ts +1 -0
  78. package/esm/shaders/hpmath.js.generated.js +4 -1
  79. package/esm/shaders/pointMark.consts.generated.d.ts +4 -0
  80. package/esm/shaders/pointMark.consts.generated.js +6 -0
  81. package/esm/shaders/pointMark.generated.d.ts +5 -0
  82. package/esm/shaders/pointMark.generated.js +7 -0
  83. package/esm/shaders/pointMark.iface.generated.d.ts +55 -0
  84. package/esm/shaders/pointMark.iface.generated.js +100 -0
  85. package/esm/shaders/pointMark.js.generated.d.ts +1 -0
  86. package/esm/shaders/pointMark.js.generated.js +16 -0
  87. package/esm/shaders/rowRect.generated.d.ts +0 -1
  88. package/esm/shaders/rowRect.generated.js +0 -1
  89. package/esm/shaders/spanMark.generated.d.ts +4 -0
  90. package/esm/shaders/spanMark.generated.js +6 -0
  91. package/esm/shaders/spanMark.iface.generated.d.ts +58 -0
  92. package/esm/shaders/spanMark.iface.generated.js +122 -0
  93. package/esm/sharedBackendKey.d.ts +18 -0
  94. package/esm/{keyedRenderingBackend.js → sharedBackendKey.js} +5 -0
  95. package/esm/slangPass.d.ts +2 -0
  96. package/esm/slangPass.js +2 -0
  97. package/package.json +14 -4
  98. package/src/shaders/capsule.js.generated.ts +14 -0
  99. package/src/shaders/capsule.slang +12 -4
  100. package/src/shaders/colorPack.slang +5 -1
  101. package/src/shaders/coverageBand.slang +38 -15
  102. package/src/shaders/coverageBar.generated.ts +2 -2
  103. package/src/shaders/coverageBar.slang +9 -0
  104. package/src/shaders/coverageMod.generated.ts +2 -2
  105. package/src/shaders/coverageSnp.generated.ts +2 -2
  106. package/src/shaders/hpmath.js.generated.ts +5 -1
  107. package/src/shaders/hpmath.slang +29 -10
  108. package/src/shaders/pointMark.consts.generated.ts +10 -0
  109. package/src/shaders/pointMark.generated.ts +12 -0
  110. package/src/shaders/pointMark.iface.generated.ts +146 -0
  111. package/src/shaders/pointMark.js.generated.ts +21 -0
  112. package/src/shaders/pointMark.slang +192 -0
  113. package/src/shaders/rowRect.generated.ts +0 -2
  114. package/src/shaders/rowRect.slang +33 -44
  115. package/src/shaders/spanMark.generated.ts +10 -0
  116. package/src/shaders/spanMark.iface.generated.ts +168 -0
  117. package/src/shaders/spanMark.slang +22 -0
  118. package/esm/globalRenderingBackend.d.ts +0 -90
  119. package/esm/globalRenderingBackend.js +0 -53
  120. package/esm/keyedRenderingBackend.d.ts +0 -41
@@ -0,0 +1,192 @@
1
+ // The `point` mark shape: one glyph per instance, placed at a genomic x and at
2
+ // a y read off a per-instance value through a linear domain scale. The glyph
3
+ // channel picks between a disc, an inverted triangle and a diamond, and an
4
+ // instance whose x..x2 extent is wider than the glyph draws as a horizontal bar
5
+ // instead — the shape's own answer to "this datum has extent".
6
+ //
7
+ // All four are a 6-vertex quad; the fragment shader carves the shape.
8
+ //! targets: wgsl, glsl
9
+ //! export-consts: DIAMOND_GLYPH_SCALE, GLYPH_DISC, GLYPH_TRIANGLE, GLYPH_DIAMOND
10
+ //! js-export: valueToYPx
11
+
12
+ import antialias;
13
+ import hpmath;
14
+ import colorPack;
15
+ import pointGlyph;
16
+
17
+ public static const uint VERTS_PER_INSTANCE = 6u;
18
+
19
+ struct PointMarkInstance {
20
+ uint x : ATTR0;
21
+ uint x2 : ATTR1;
22
+ float y : ATTR2;
23
+ uint color : ATTR3;
24
+ uint glyph : ATTR4;
25
+ };
26
+
27
+ struct Uniforms {
28
+ float3 bpRangeX;
29
+ float canvasHeight;
30
+ float domainMin;
31
+ float domainMax;
32
+ float zero;
33
+ float viewportWidth;
34
+ float radiusPx;
35
+ // Read by the glyph pad alone; every other length in this block is CSS px.
36
+ float devicePixelRatio;
37
+ };
38
+ [[vk::binding(1, 0)]] ConstantBuffer<Uniforms> u;
39
+
40
+ float bpToClipX(uint bp, Uniforms u) {
41
+ return hpToClipX(hpSplitUint(bp), u.bpRangeX, u.zero);
42
+ }
43
+
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
+ struct VsOut {
68
+ float4 position : SV_Position;
69
+ float4 color : COLOR0;
70
+ float2 localPos : TEXCOORD0;
71
+ // Shape discriminator passed to the fragment shader: 0 = disc, 1 = diamond,
72
+ // 2 = inverted triangle, 3 = solid bar.
73
+ nointerpolation float shape : TEXCOORD1;
74
+ };
75
+
76
+ // Solid ink each side of a bar's genomic span, in CSS px, so a run of adjacent
77
+ // bars abuts. SHAPE_BAR's fragment coverage is a constant 1.
78
+ static const float BAR_OVERDRAW_PX = 1.0;
79
+
80
+ // Shape discriminators for the fragment shader. SMALL_POINT_MAX_DIAMETER and
81
+ // the disc SDF come from the shared pointGlyph module so they stay in lockstep
82
+ // with wiggle.slang.
83
+ static const float SHAPE_DISC = 0.0;
84
+ static const float SHAPE_DIAMOND = 1.0;
85
+ static const float SHAPE_TRI = 2.0;
86
+ // >2.5 in the fragment shader = flat solid fill (extent bar, or the tiny
87
+ // square below).
88
+ static const float SHAPE_BAR = 3.0;
89
+ // 1/sqrt(5): normalizes the two slanted edges of the inverted triangle so the
90
+ // edge functions read as true perpendicular distances for antialiasing.
91
+ static const float INV_SQRT5 = 0.4472136;
92
+ // The diamond is the shape's "singled out" glyph and draws visibly larger than
93
+ // the others, so it reads as the one that matters at a glance rather than as a
94
+ // colour swap. LocusZoom's lead-SNP convention, generalized.
95
+ static const float DIAMOND_GLYPH_SCALE = 1.6;
96
+
97
+ // The glyph channel's values. The numbering is a contract between whatever
98
+ // packs the channel, the Canvas2D painter and the branches below, so it is
99
+ // authored once here and `//! export-consts`ed.
100
+ static const uint GLYPH_DISC = 0u;
101
+ static const uint GLYPH_TRIANGLE = 1u;
102
+ static const uint GLYPH_DIAMOND = 2u;
103
+
104
+ [shader("vertex")]
105
+ VsOut vs_main(PointMarkInstance inst, uint vid : SV_VertexID) {
106
+ uint v = vid % 6u;
107
+
108
+ // Two-triangle quad corner (0/1 per axis); shared with the whole fleet.
109
+ float2 q = quadLocal(v);
110
+ float2 local = q * 2.0 - 1.0;
111
+ bool xZero = q.x < 0.5;
112
+
113
+ float startClipX = bpToClipX(inst.x, u);
114
+ float endClipX = bpToClipX(inst.x2, u);
115
+ float yPx = valueToYPx(inst.y, u.domainMin, u.domainMax, u.canvasHeight);
116
+ float centerClipY = yPxToClipY(yPx, u.canvasHeight);
117
+
118
+ // Genomic span in CSS px (clip width spans -1..1 across viewportWidth).
119
+ // abs() because reversal is baked into bpRangeX (negated length), so
120
+ // endClipX < startClipX on reversed regions — matches wiggle.slang.
121
+ float spanPx = clipLenToPx(abs(endClipX - startClipX), u.viewportWidth);
122
+ bool isBar = spanPx > u.radiusPx * 2.0;
123
+
124
+ VsOut o;
125
+ o.color = unpackRGBA(inst.color);
126
+ if (isBar) {
127
+ // Horizontal bar from x→x2, height 2*radiusPx px, centered on the value.
128
+ float overdrawClipX = pxToClipLen(BAR_OVERDRAW_PX, u.viewportWidth);
129
+ // min/max so a reversed region (endClipX < startClipX) still spans the full
130
+ // bar extent instead of drawing an inverted quad — matches wiggle.slang.
131
+ float x = xZero ? (min(startClipX, endClipX) - overdrawClipX)
132
+ : (max(startClipX, endClipX) + overdrawClipX);
133
+ float halfBandPx = u.radiusPx;
134
+ float y = centerClipY + pxToClipLen(local.y * halfBandPx, u.canvasHeight);
135
+ o.position = float4(x, y, 0.0, 1.0);
136
+ o.localPos = local;
137
+ o.shape = SHAPE_BAR;
138
+ } else if (inst.glyph == GLYPH_DISC && u.radiusPx * 2.0 <= SMALL_POINT_MAX_DIAMETER) {
139
+ // tiny disc: pixel-snapped crisp filled square (no AA disc). The triangle
140
+ // and the diamond keep their distinct marker even when small.
141
+ float diamPx = u.radiusPx * 2.0;
142
+ o.position = crispSquareCornerClip(startClipX, yPx, diamPx, q,
143
+ u.viewportWidth, u.canvasHeight);
144
+ o.localPos = local;
145
+ o.shape = SHAPE_BAR;
146
+ } else {
147
+ float expand = discExpand(u.radiusPx, u.devicePixelRatio);
148
+ float glyphScale = inst.glyph == GLYPH_DIAMOND ? DIAMOND_GLYPH_SCALE : 1.0;
149
+ float halfPx = u.radiusPx * expand * glyphScale;
150
+ float dx = pxToClipLen(local.x * halfPx, u.viewportWidth);
151
+ float dy = pxToClipLen(local.y * halfPx, u.canvasHeight);
152
+ o.position = float4(startClipX + dx, centerClipY + dy, 0.0, 1.0);
153
+ o.localPos = local * expand;
154
+ o.shape = inst.glyph == GLYPH_TRIANGLE ? SHAPE_TRI
155
+ : (inst.glyph == GLYPH_DIAMOND ? SHAPE_DIAMOND : SHAPE_DISC);
156
+ }
157
+ return o;
158
+ }
159
+
160
+ [shader("fragment")]
161
+ float4 fs_main(VsOut fragIn) : SV_Target {
162
+ // Each glyph only picks its SDF here; the AA derivative is taken once after
163
+ // the branch, because WGSL rejects a derivative under a branch on a varying
164
+ // ("must only be called from uniform control flow"). shape is constant per
165
+ // primitive, so the shared ramp is exact.
166
+ float2 p = fragIn.localPos;
167
+ float coverage;
168
+ if (fragIn.shape > 2.5) {
169
+ // Extent bar: solid fill, no marker AA. A constant SDF has zero gradient,
170
+ // so the ramp below saturates to alpha 1.
171
+ coverage = 1.0;
172
+ } else if (fragIn.shape > 1.5) {
173
+ // Inverted triangle: top edge (-1,1)-(1,1), apex (0,-1). Each edge's signed
174
+ // distance is positive inside; the min is the SDF used for the AA fade.
175
+ float dTop = 1.0 - p.y;
176
+ float dLeft = (2.0 * p.x + p.y + 1.0) * INV_SQRT5;
177
+ float dRight = (-2.0 * p.x + p.y + 1.0) * INV_SQRT5;
178
+ coverage = min(dTop, min(dLeft, dRight));
179
+ } else if (fragIn.shape > 0.5) {
180
+ // Diamond (rhombus): L1-norm distance from center, positive inside, zero at
181
+ // the four edges.
182
+ coverage = 1.0 - (abs(p.x) + abs(p.y));
183
+ } else {
184
+ coverage = discCoverage(p);
185
+ }
186
+ // One output pixel of ramp, from the SDF's own gradient — which is what lets
187
+ // the four shapes above share a single line despite the diamond's L1 norm
188
+ // carrying a sqrt(2) the others don't. See antialias.slang's glyphEdgeAlpha.
189
+ float alpha = glyphEdgeAlpha(coverage);
190
+ if (alpha <= 0.0) discard;
191
+ return float4(fragIn.color.rgb, fragIn.color.a * alpha);
192
+ }
@@ -3,6 +3,4 @@
3
3
 
4
4
  export const MIN_DRAWN_ROW_PX = 1
5
5
 
6
- export const MIN_DRAWN_CELL_PX = 1
7
-
8
6
  export const MULTI_ROW_MIN_CELL_PX = 2
@@ -1,6 +1,6 @@
1
1
  // Shared "row of colored rects" primitive: one instance is a rectangle
2
- // spanning [startBp, endBp) in absolute genomic coords, drawn on the row given
3
- // by `rowIndex` and filled with a per-instance packed ABGR color.
2
+ // spanning [x, x2) in absolute genomic coords, drawn on the row given by
3
+ // `row` and filled with a per-instance packed ABGR color.
4
4
  //
5
5
  // Two renderers draw exactly this: the MAF alignment renderer (run-merged
6
6
  // per-base cells) and the multi-row feature painter / "chromosome painting"
@@ -11,13 +11,14 @@
11
11
  // Row placement comes from uniforms rather than the instance data, so a
12
12
  // rowHeight / rowProportion change re-renders without a refetch.
13
13
  //
14
- // What the callers legitimately differ on is the min-width floor: both the
15
- // width they write into `minCellPx` and the *unit* they measure it in, via
16
- // `minCellDenomPx` (CSS px vs device px) — see GpuMafRenderer's note. Those are
17
- // uniform-write decisions, not a reason for two shaders. The unit is also
18
- // unsettled, and device px additionally makes MAF's floor depend on the
19
- // reader's monitor: `agent-docs/ideas/maf-subpixel-cells.md`.
20
- //! export-consts: MIN_DRAWN_ROW_PX, MIN_DRAWN_CELL_PX, MULTI_ROW_MIN_CELL_PX
14
+ // What the callers legitimately differ on is the min-width floor they write
15
+ // into `minCellPx` — a uniform-write decision, not a reason for two shaders.
16
+ // Whether to floor at all turns on whether the caller's marks TILE: MAF's cells
17
+ // do, so a sub-pixel one is read as part of the run around it and widening it
18
+ // paints ink the alignment does not contain (measured, and MAF now passes 0 —
19
+ // `agent-docs/reference/MAF_SUBPIXEL_CELLS.md`). The multi-row painter's features
20
+ // are sparse intervals with nothing beside them, so it floors.
21
+ //! export-consts: MIN_DRAWN_ROW_PX, MULTI_ROW_MIN_CELL_PX
21
22
  //! js-export: drawnRowHeightPx, rowBandOffsetPx
22
23
  module rowRect;
23
24
 
@@ -25,10 +26,10 @@ import colorPack;
25
26
  import hpmath;
26
27
 
27
28
  public struct RowRectInstance {
28
- public uint startBp : ATTR0;
29
- public uint endBp : ATTR1;
30
- public uint rowIndex : ATTR2;
31
- public uint color : ATTR3; // packed ABGR
29
+ public uint x : ATTR0;
30
+ public uint x2 : ATTR1;
31
+ public uint row : ATTR2;
32
+ public uint color : ATTR3; // packed ABGR
32
33
  };
33
34
 
34
35
  public struct RowRectUniforms {
@@ -36,16 +37,13 @@ public struct RowRectUniforms {
36
37
  public float canvasHeight; // CSS px
37
38
  // The width `minCellPx` is measured against, and so the thing that decides
38
39
  // what unit `minCellPx` IS. Named for that rather than for the viewport
39
- // because the three callers do not agree: multi-row and wiggle density pass
40
- // a CSS width (a 1 CSS px floor), MAF passes a device one (1 device px, so
41
- // half a CSS px on a retina screen and a floor that moves with the monitor).
42
- // That disagreement is live and parked, not settled —
43
- // agent-docs/ideas/maf-subpixel-cells.md — so the name carries it to every
44
- // call site instead of a comment forty lines away.
40
+ // because the callers that floor pass a CSS width. Pass a DEVICE width and
41
+ // the floor moves with the reader's monitor — MAF did, and rendered
42
+ // measurably differently on a retina screen until it stopped flooring.
45
43
  public float minCellDenomPx;
46
- // Narrowest a cell is painted, in whatever unit `minCellDenomPx` is in.
47
- // MIN_DRAWN_CELL_PX is the floor a caller takes when it has no reason to
48
- // widen further; the multi-row painter does (see MULTI_ROW_MIN_CELL_PX).
44
+ // Narrowest a cell is painted, in whatever unit `minCellDenomPx` is in. Zero
45
+ // is a no-op and is what a tiling caller writes (MAF); the multi-row painter
46
+ // names its own width, MULTI_ROW_MIN_CELL_PX.
49
47
  public float minCellPx;
50
48
  public float zero; // always 0.0 — prevents hp-split collapse
51
49
  public float rowHeight; // CSS px per row
@@ -62,24 +60,16 @@ public struct RowRectVsOut {
62
60
  public float4 color : COLOR0;
63
61
  };
64
62
 
65
- // Thinnest a row is ever painted. Vertical twin of MIN_DRAWN_CELL_PX below: a
66
- // row thinner than a pixel can miss every pixel center and disappear, so
67
- // without this rows drop out as they get denser instead of just getting
68
- // thinner. Floored, neighbours overlap instead.
69
- static const float MIN_DRAWN_ROW_PX = 1.0;
70
-
71
- // Thinnest a cell can be painted and still be drawable at all, for the same
72
- // reason as the row floor above on the other axis. A caller writes this into
73
- // `minCellPx` when a cell being *visible* is not its problem, only a cell
74
- // existing; one whose whole encoding is carried by sub-pixel cells asks for
75
- // more (MULTI_ROW_MIN_CELL_PX).
63
+ // Thinnest a row is ever painted: a row thinner than a pixel can miss every
64
+ // pixel center and disappear, so without this rows drop out as they get denser
65
+ // instead of just getting thinner. Floored, neighbours overlap instead.
76
66
  //
77
- // One caller mirrors it on the Canvas2D side and one deliberately does not: the
78
- // multi-row painter floors, MAF draws cells at their natural sub-pixel width.
79
- // That asymmetry is real and parked in `agent-docs/ideas/maf-subpixel-cells.md`;
80
- // sharing the constant does not decide it, it just stops the value being
81
- // anonymous in the places that DO agree.
82
- static const float MIN_DRAWN_CELL_PX = 1.0;
67
+ // There is no X-axis twin. A row is a container the reader is counting, so
68
+ // losing one loses a species; a cell is a sample of a tiling, so losing one to
69
+ // the blend is the honest answer and flooring it invents ink
70
+ // (`agent-docs/reference/MAF_SUBPIXEL_CELLS.md`). The caller that does want an X
71
+ // floor names its own width, below.
72
+ static const float MIN_DRAWN_ROW_PX = 1.0;
83
73
 
84
74
  // What the multi-row painter ("chromosome painting") writes into `minCellPx`.
85
75
  //
@@ -91,9 +81,9 @@ static const float MIN_DRAWN_CELL_PX = 1.0;
91
81
  // across two, so neither reaches full opacity — the tick reads as a smudge, and
92
82
  // the eye scanning for structure across the rows finds none.
93
83
  //
94
- // Both floors distort width; that is what a floor is. This one distorts more,
95
- // and only where the alternative was showing nothing legible: a feature wide
96
- // enough to matter is already past it.
84
+ // A floor distorts width; that is what a floor is. This is the only caller that
85
+ // takes the trade, and only where the alternative was showing nothing legible:
86
+ // a feature wide enough to matter is already past it.
97
87
  static const float MULTI_ROW_MIN_CELL_PX = 2.0;
98
88
 
99
89
  public struct RowBand {
@@ -153,8 +143,7 @@ public RowRectVsOut rowRectVertex(
153
143
  RowRectInstance inst, uint vid, RowRectUniforms u
154
144
  ) {
155
145
  RowRectVsOut o;
156
- o.position = rowRectClipPos(
157
- inst.startBp, inst.endBp, float(inst.rowIndex), vid, u);
146
+ o.position = rowRectClipPos(inst.x, inst.x2, float(inst.row), vid, u);
158
147
  o.color = unpackRGBA(inst.color);
159
148
  return o;
160
149
  }
@@ -0,0 +1,10 @@
1
+ // AUTO-GENERATED by packages/shader-tools/src/shader-codegen from spanMark.slang.
2
+ // Do not edit. Run `pnpm gen:shaders` to regenerate.
3
+
4
+ export const WGSL_SOURCE = "struct RowRectUniforms_std140_0\n{\n @align(16) bpRangeX_0 : vec3<f32>,\n @align(4) canvasHeight_0 : f32,\n @align(16) minCellDenomPx_0 : f32,\n @align(4) minCellPx_0 : f32,\n @align(8) zero_0 : f32,\n @align(4) rowHeight_0 : f32,\n @align(16) rowProportion_0 : f32,\n @align(4) scrollTop_0 : f32,\n};\n\n@binding(1) @group(0) var<uniform> u_0 : RowRectUniforms_std140_0;\nfn quadLocal_0( vid_0 : u32) -> vec2<f32>\n{\n var v_0 : u32 = vid_0 % u32(6);\n var _S1 : bool = v_0 == u32(0);\n var _S2 : bool;\n if(_S1)\n {\n _S2 = true;\n }\n else\n {\n _S2 = v_0 == u32(2);\n }\n if(_S2)\n {\n _S2 = true;\n }\n else\n {\n _S2 = v_0 == u32(3);\n }\n var _S3 : f32;\n if(_S2)\n {\n _S3 = 0.0f;\n }\n else\n {\n _S3 = 1.0f;\n }\n if(_S1)\n {\n _S2 = true;\n }\n else\n {\n _S2 = v_0 == u32(1);\n }\n if(_S2)\n {\n _S2 = true;\n }\n else\n {\n _S2 = v_0 == u32(4);\n }\n var _S4 : f32;\n if(_S2)\n {\n _S4 = 0.0f;\n }\n else\n {\n _S4 = 1.0f;\n }\n return vec2<f32>(_S3, _S4);\n}\n\nfn drawnRowHeightPx_0( rowHeight_1 : f32, rowProportion_1 : f32) -> f32\n{\n return max(rowHeight_1 * rowProportion_1, 1.0f);\n}\n\nfn rowBandOffsetPx_0( rowHeight_2 : f32, rowProportion_2 : f32) -> f32\n{\n return (rowHeight_2 - drawnRowHeightPx_0(rowHeight_2, rowProportion_2)) * 0.5f;\n}\n\nstruct RowBand_0\n{\n height_0 : f32,\n offset_0 : f32,\n};\n\nfn rowBandPx_0( rowHeight_3 : f32, rowProportion_3 : f32) -> RowBand_0\n{\n var band_0 : RowBand_0;\n band_0.height_0 = drawnRowHeightPx_0(rowHeight_3, rowProportion_3);\n band_0.offset_0 = rowBandOffsetPx_0(rowHeight_3, rowProportion_3);\n return band_0;\n}\n\nfn hpSplitUint_0( value_0 : u32) -> vec2<f32>\n{\n var lo_0 : u32 = (value_0 & (u32(4095)));\n return vec2<f32>(f32(value_0 - lo_0), f32(lo_0));\n}\n\nfn hpToClipX_0( splitPos_0 : vec2<f32>, bpRange_0 : vec3<f32>, hpZero_0 : f32) -> f32\n{\n var step_0 : f32 = 2.0f / bpRange_0.z;\n var _S5 : f32 = - (1.0f / hpZero_0);\n return dot(vec3<f32>(-1.0f, max(splitPos_0.x - bpRange_0.x, _S5), max(splitPos_0.y - bpRange_0.y, _S5)), vec3<f32>(1.0f, step_0, step_0));\n}\n\nfn extendToMinWidthPx_0( x1_0 : f32, x2_0 : f32, minWidth_0 : f32) -> f32\n{\n var dx_0 : f32 = x2_0 - x1_0;\n var _S6 : f32;\n if((abs(dx_0)) < minWidth_0)\n {\n if(dx_0 < 0.0f)\n {\n _S6 = - minWidth_0;\n }\n else\n {\n _S6 = minWidth_0;\n }\n _S6 = x1_0 + _S6;\n }\n else\n {\n _S6 = x2_0;\n }\n return _S6;\n}\n\nfn extendToMinWidthX_0( x1_1 : f32, x2_1 : f32, minWidthPx_0 : f32, canvasWidth_0 : f32) -> f32\n{\n return extendToMinWidthPx_0(x1_1, x2_1, minWidthPx_0 * 2.0f / canvasWidth_0);\n}\n\nfn yPxToClipY_0( yPx_0 : f32, canvasHeight_1 : f32) -> f32\n{\n return 1.0f - yPx_0 / canvasHeight_1 * 2.0f;\n}\n\nfn rowRectClipPos_0( startBp_0 : u32, endBp_0 : u32, rowIndex_0 : f32, vid_1 : u32, u_1 : ptr<function, RowRectUniforms_std140_0>) -> vec4<f32>\n{\n var local_0 : vec2<f32> = quadLocal_0(vid_1);\n var band_1 : RowBand_0 = rowBandPx_0((*u_1).rowHeight_0, (*u_1).rowProportion_0);\n var rowY_0 : f32 = band_1.offset_0 + (*u_1).rowHeight_0 * rowIndex_0 - (*u_1).scrollTop_0;\n var sx1_0 : f32 = hpToClipX_0(hpSplitUint_0(startBp_0), (*u_1).bpRangeX_0, (*u_1).zero_0);\n return vec4<f32>(mix(sx1_0, extendToMinWidthX_0(sx1_0, hpToClipX_0(hpSplitUint_0(endBp_0), (*u_1).bpRangeX_0, (*u_1).zero_0), (*u_1).minCellPx_0, (*u_1).minCellDenomPx_0), local_0.x), yPxToClipY_0(mix(rowY_0, rowY_0 + band_1.height_0, local_0.y), (*u_1).canvasHeight_0), 0.0f, 1.0f);\n}\n\nfn unpackRGBA_0( c_0 : u32) -> vec4<f32>\n{\n return vec4<f32>(f32((((c_0 >> (u32(0)))) & (u32(255)))), f32((((c_0 >> (u32(8)))) & (u32(255)))), f32((((c_0 >> (u32(16)))) & (u32(255)))), f32((((c_0 >> (u32(24)))) & (u32(255))))) / vec4<f32>(255.0f);\n}\n\nstruct RowRectVsOut_0\n{\n @builtin(position) position_0 : vec4<f32>,\n @location(0) color_0 : vec4<f32>,\n};\n\nstruct RowRectInstance_0\n{\n x_0 : u32,\n x2_2 : u32,\n row_0 : u32,\n color_1 : u32,\n};\n\nfn rowRectVertex_0( inst_0 : RowRectInstance_0, vid_2 : u32, u_2 : ptr<function, RowRectUniforms_std140_0>) -> RowRectVsOut_0\n{\n var o_0 : RowRectVsOut_0;\n var _S7 : vec4<f32> = rowRectClipPos_0(inst_0.x_0, inst_0.x2_2, f32(inst_0.row_0), vid_2, &((*u_2)));\n o_0.position_0 = _S7;\n o_0.color_0 = unpackRGBA_0(inst_0.color_1);\n return o_0;\n}\n\nstruct vertexInput_0\n{\n @location(0) x_1 : u32,\n @location(1) x2_3 : u32,\n @location(2) row_1 : u32,\n @location(3) color_2 : u32,\n};\n\n@vertex\nfn vs_main( _S8 : vertexInput_0, @builtin(vertex_index) vid_3 : u32) -> RowRectVsOut_0\n{\n var _S9 : RowRectInstance_0 = RowRectInstance_0( _S8.x_1, _S8.x2_3, _S8.row_1, _S8.color_2 );\n var _S10 : RowRectUniforms_std140_0 = u_0;\n var _S11 : RowRectVsOut_0 = rowRectVertex_0(_S9, vid_3, &(_S10));\n return _S11;\n}\n\nstruct pixelOutput_0\n{\n @location(0) output_0 : vec4<f32>,\n};\n\nstruct pixelInput_0\n{\n @location(0) color_3 : vec4<f32>,\n};\n\n@fragment\nfn fs_main( _S12 : pixelInput_0, @builtin(position) position_1 : vec4<f32>) -> pixelOutput_0\n{\n var _S13 : pixelOutput_0 = pixelOutput_0( _S12.color_3 );\n return _S13;\n}\n\n"
5
+
6
+ export const GLSL_VERTEX = "#version 300 es\nprecision highp float;\nprecision highp int;\nstruct RowRectUniforms_0\n{\n vec3 bpRangeX_0;\n float canvasHeight_0;\n float minCellDenomPx_0;\n float minCellPx_0;\n float zero_0;\n float rowHeight_0;\n float rowProportion_0;\n float scrollTop_0;\n};\n\n\nlayout(std140) uniform Uniforms\n{\n vec3 bpRangeX_0;\n float canvasHeight_0;\n float minCellDenomPx_0;\n float minCellPx_0;\n float zero_0;\n float rowHeight_0;\n float rowProportion_0;\n float scrollTop_0;\n}u_0;\n\nvec2 quadLocal_0(uint vid_0)\n{\n\n uint v_0 = vid_0 % 6U;\n\n bool _S1 = v_0 == 0U;\n\n bool _S2;\n\n if(_S1)\n {\n\n _S2 = true;\n\n }\n else\n {\n\n _S2 = v_0 == 2U;\n\n }\n\n if(_S2)\n {\n\n _S2 = true;\n\n }\n else\n {\n\n _S2 = v_0 == 3U;\n\n }\n\n float _S3;\n\n if(_S2)\n {\n\n _S3 = 0.0;\n\n }\n else\n {\n\n _S3 = 1.0;\n\n }\n if(_S1)\n {\n\n _S2 = true;\n\n }\n else\n {\n\n _S2 = v_0 == 1U;\n\n }\n\n if(_S2)\n {\n\n _S2 = true;\n\n }\n else\n {\n\n _S2 = v_0 == 4U;\n\n }\n\n float _S4;\n\n if(_S2)\n {\n\n _S4 = 0.0;\n\n }\n else\n {\n\n _S4 = 1.0;\n\n }\n\n return vec2(_S3, _S4);\n}\n\n\nfloat drawnRowHeightPx_0(float rowHeight_1, float rowProportion_1)\n{\n\n return max(rowHeight_1 * rowProportion_1, 1.0);\n}\n\nfloat rowBandOffsetPx_0(float rowHeight_2, float rowProportion_2)\n{\n\n return (rowHeight_2 - drawnRowHeightPx_0(rowHeight_2, rowProportion_2)) * 0.5;\n}\n\n\nstruct RowBand_0\n{\n float height_0;\n float offset_0;\n};\n\n\nRowBand_0 rowBandPx_0(float rowHeight_3, float rowProportion_3)\n{\n\n RowBand_0 band_0;\n band_0.height_0 = drawnRowHeightPx_0(rowHeight_3, rowProportion_3);\n band_0.offset_0 = rowBandOffsetPx_0(rowHeight_3, rowProportion_3);\n return band_0;\n}\n\n\nvec2 hpSplitUint_0(uint value_0)\n{\n\n uint lo_0 = value_0 & 4095U;\n\n return vec2(float(value_0 - lo_0), float(lo_0));\n}\n\n\nfloat hpToClipX_0(vec2 splitPos_0, vec3 bpRange_0, float hpZero_0)\n{\n float step_0 = 2.0 / bpRange_0.z;\n float _S5 = - (1.0 / hpZero_0);\n\n return dot(vec3(-1.0, max(splitPos_0.x - bpRange_0.x, _S5), max(splitPos_0.y - bpRange_0.y, _S5)), vec3(1.0, step_0, step_0));\n}\n\n\nfloat extendToMinWidthPx_0(float x1_0, float x2_0, float minWidth_0)\n{\n\n float dx_0 = x2_0 - x1_0;\n\n float _S6;\n if((abs(dx_0)) < minWidth_0)\n {\n\n if(dx_0 < 0.0)\n {\n\n _S6 = - minWidth_0;\n\n }\n else\n {\n\n _S6 = minWidth_0;\n\n }\n\n _S6 = x1_0 + _S6;\n\n }\n else\n {\n\n _S6 = x2_0;\n\n }\n\n return _S6;\n}\n\nfloat extendToMinWidthX_0(float x1_1, float x2_1, float minWidthPx_0, float canvasWidth_0)\n{\n\n return extendToMinWidthPx_0(x1_1, x2_1, minWidthPx_0 * 2.0 / canvasWidth_0);\n}\n\n\nfloat yPxToClipY_0(float yPx_0, float canvasHeight_1)\n{\n\n return 1.0 - yPx_0 / canvasHeight_1 * 2.0;\n}\n\n\nvec4 rowRectClipPos_0(uint startBp_0, uint endBp_0, float rowIndex_0, uint vid_1, RowRectUniforms_0 u_1)\n{\n\n vec2 local_0 = quadLocal_0(vid_1);\n\n RowBand_0 band_1 = rowBandPx_0(u_1.rowHeight_0, u_1.rowProportion_0);\n float rowY_0 = band_1.offset_0 + u_1.rowHeight_0 * rowIndex_0 - u_1.scrollTop_0;\n\n float sx1_0 = hpToClipX_0(hpSplitUint_0(startBp_0), u_1.bpRangeX_0, u_1.zero_0);\n\n return vec4(mix(sx1_0, extendToMinWidthX_0(sx1_0, hpToClipX_0(hpSplitUint_0(endBp_0), u_1.bpRangeX_0, u_1.zero_0), u_1.minCellPx_0, u_1.minCellDenomPx_0), local_0.x), yPxToClipY_0(mix(rowY_0, rowY_0 + band_1.height_0, local_0.y), u_1.canvasHeight_0), 0.0, 1.0);\n}\n\n\nvec4 unpackRGBA_0(uint c_0)\n{\n\n return vec4(float((c_0 >> 0U) & 255U), float((c_0 >> 8U) & 255U), float((c_0 >> 16U) & 255U), float((c_0 >> 24U) & 255U)) / 255.0;\n}\n\n\nstruct RowRectVsOut_0\n{\n vec4 position_0;\n vec4 color_0;\n};\n\n\nstruct RowRectInstance_0\n{\n uint x_0;\n uint x2_2;\n uint row_0;\n uint color_1;\n};\n\n\nRowRectVsOut_0 rowRectVertex_0(RowRectInstance_0 inst_0, uint vid_2, RowRectUniforms_0 u_2)\n{\n\n RowRectVsOut_0 o_0;\n o_0.position_0 = rowRectClipPos_0(inst_0.x_0, inst_0.x2_2, float(inst_0.row_0), vid_2, u_2);\n o_0.color_0 = unpackRGBA_0(inst_0.color_1);\n return o_0;\n}\n\n\nout vec4 v_color;\n\n\nlayout(location = 0)\nin uint a_x;\n\n\nlayout(location = 1)\nin uint a_x2;\n\n\nlayout(location = 2)\nin uint a_row;\n\n\nlayout(location = 3)\nin uint a_color;\n\n\nvoid main()\n{\n\n RowRectUniforms_0 _S7 = RowRectUniforms_0(u_0.bpRangeX_0, u_0.canvasHeight_0, u_0.minCellDenomPx_0, u_0.minCellPx_0, u_0.zero_0, u_0.rowHeight_0, u_0.rowProportion_0, u_0.scrollTop_0);\n\n RowRectInstance_0 _S8 = RowRectInstance_0(a_x, a_x2, a_row, a_color);\n RowRectVsOut_0 _S9 = rowRectVertex_0(_S8, uint(gl_VertexID), _S7);\n\n gl_Position = _S9.position_0;\n\n v_color = _S9.color_0;\n\n return;\n}\n\n"
7
+
8
+ export const GLSL_FRAGMENT = "#version 300 es\nprecision highp float;\nprecision highp int;\nlayout(location = 0)\nout vec4 entryPointParam_fs_main_0;\n\n\nin vec4 v_color;\n\n\nvoid main()\n{\n\n entryPointParam_fs_main_0 = v_color;\n\n return;\n}\n\n"
9
+
10
+ export * from './spanMark.iface.generated.ts'
@@ -0,0 +1,168 @@
1
+ // AUTO-GENERATED by packages/shader-tools/src/shader-codegen from spanMark.slang.
2
+ // Do not edit. Run `pnpm gen:shaders` to regenerate.
3
+
4
+ import type { VertexAttributeLayout, ShaderBinding } from '@jbrowse/render-core/hal'
5
+
6
+ export const BINDINGS: readonly ShaderBinding[] = [
7
+ { index: 1, kind: 'uniform', name: 'u' },
8
+ ]
9
+
10
+ export const VERTS_PER_INSTANCE = 6
11
+
12
+ export const UNIFORMS_SIZE_BYTES = 48
13
+
14
+ // Word indices into a Float32Array view over the uniform buffer.
15
+ export const UNIFORM_OFFSET_F32 = {
16
+ bpRangeX: 0,
17
+ canvasHeight: 3,
18
+ minCellDenomPx: 4,
19
+ minCellPx: 5,
20
+ zero: 6,
21
+ rowHeight: 7,
22
+ rowProportion: 8,
23
+ scrollTop: 9,
24
+ } as const
25
+
26
+
27
+ export interface Uniforms {
28
+ bpRangeX: [number, number, number]
29
+ canvasHeight: number
30
+ minCellDenomPx: number
31
+ minCellPx: number
32
+ zero: number
33
+ rowHeight: number
34
+ rowProportion: number
35
+ scrollTop: number
36
+ }
37
+
38
+ export function writeUniforms(buf: ArrayBuffer, uniforms: Uniforms) {
39
+ const f32 = new Float32Array(buf)
40
+ f32[0] = uniforms.bpRangeX[0]
41
+ f32[1] = uniforms.bpRangeX[1]
42
+ f32[2] = uniforms.bpRangeX[2]
43
+ f32[3] = uniforms.canvasHeight
44
+ f32[4] = uniforms.minCellDenomPx
45
+ f32[5] = uniforms.minCellPx
46
+ f32[6] = uniforms.zero
47
+ f32[7] = uniforms.rowHeight
48
+ f32[8] = uniforms.rowProportion
49
+ f32[9] = uniforms.scrollTop
50
+ }
51
+
52
+ export const INSTANCE_STRIDE_BYTES = 16
53
+ export const INSTANCE_STRIDE_WORDS = 4
54
+
55
+ // Word indices into a Uint32Array view over the instance buffer.
56
+ export const INSTANCE_OFFSET_U32 = {
57
+ x: 0,
58
+ x2: 1,
59
+ row: 2,
60
+ color: 3,
61
+ } as const
62
+
63
+ export const VERTEX_ATTRIBUTES: readonly VertexAttributeLayout[] = [
64
+ { name: 'a_x', components: 1, type: 'uint', offsetBytes: 0, integer: true },
65
+ { name: 'a_x2', components: 1, type: 'uint', offsetBytes: 4, integer: true },
66
+ { name: 'a_row', components: 1, type: 'uint', offsetBytes: 8, integer: true },
67
+ { name: 'a_color', components: 1, type: 'uint', offsetBytes: 12, integer: true },
68
+ ]
69
+
70
+ export interface InstanceArrays {
71
+ x: ArrayLike<number>
72
+ x2: ArrayLike<number>
73
+ row: ArrayLike<number>
74
+ color: ArrayLike<number>
75
+ }
76
+
77
+ export function packInstances(
78
+ arrays: InstanceArrays,
79
+ numInstances: number,
80
+ buf: ArrayBuffer = new ArrayBuffer(numInstances * INSTANCE_STRIDE_BYTES),
81
+ ) {
82
+ const u32 = new Uint32Array(buf)
83
+ const { x, x2, row, color } = arrays
84
+ for (let i = 0; i < numInstances; i++) {
85
+ const o = i * INSTANCE_STRIDE_WORDS
86
+ u32[o + 0] = x[i]!
87
+ u32[o + 1] = x2[i]!
88
+ u32[o + 2] = row[i]!
89
+ u32[o + 3] = color[i]!
90
+ }
91
+ return buf
92
+ }
93
+
94
+ // Instance `i`'s `x`.
95
+ export function getInstanceX(u32: Uint32Array, i: number) {
96
+ return u32[i * INSTANCE_STRIDE_WORDS]!
97
+ }
98
+
99
+ export function setInstanceX(u32: Uint32Array, i: number, v: number) {
100
+ u32[i * INSTANCE_STRIDE_WORDS] = v
101
+ }
102
+
103
+ // Instance `i`'s `x2`.
104
+ export function getInstanceX2(u32: Uint32Array, i: number) {
105
+ return u32[i * INSTANCE_STRIDE_WORDS + 1]!
106
+ }
107
+
108
+ export function setInstanceX2(u32: Uint32Array, i: number, v: number) {
109
+ u32[i * INSTANCE_STRIDE_WORDS + 1] = v
110
+ }
111
+
112
+ // Instance `i`'s `row`.
113
+ export function getInstanceRow(u32: Uint32Array, i: number) {
114
+ return u32[i * INSTANCE_STRIDE_WORDS + 2]!
115
+ }
116
+
117
+ export function setInstanceRow(u32: Uint32Array, i: number, v: number) {
118
+ u32[i * INSTANCE_STRIDE_WORDS + 2] = v
119
+ }
120
+
121
+ // Instance `i`'s `color`.
122
+ export function getInstanceColor(u32: Uint32Array, i: number) {
123
+ return u32[i * INSTANCE_STRIDE_WORDS + 3]!
124
+ }
125
+
126
+ export function setInstanceColor(u32: Uint32Array, i: number, v: number) {
127
+ u32[i * INSTANCE_STRIDE_WORDS + 3] = v
128
+ }
129
+
130
+ // Appends instances one at a time, for an encoder that cannot say up front
131
+ // how many it will emit. Seed the constructor with an upper bound and the
132
+ // common path allocates exactly once; the doubling is a correctness
133
+ // backstop, not the expected route.
134
+ export class InstanceWriter {
135
+ private buf: ArrayBuffer
136
+ private u32: Uint32Array
137
+ private capacity: number
138
+ count = 0
139
+
140
+ constructor(capacity: number) {
141
+ this.capacity = Math.max(1, capacity)
142
+ this.buf = new ArrayBuffer(this.capacity * INSTANCE_STRIDE_BYTES)
143
+ this.u32 = new Uint32Array(this.buf)
144
+ }
145
+
146
+ push(x: number, x2: number, row: number, color: number) {
147
+ if (this.count === this.capacity) {
148
+ this.capacity *= 2
149
+ const grown = new ArrayBuffer(this.capacity * INSTANCE_STRIDE_BYTES)
150
+ new Uint8Array(grown).set(new Uint8Array(this.buf))
151
+ this.buf = grown
152
+ this.u32 = new Uint32Array(grown)
153
+ }
154
+ const o = this.count * INSTANCE_STRIDE_WORDS
155
+ this.u32[o] = x
156
+ this.u32[o + 1] = x2
157
+ this.u32[o + 2] = row
158
+ this.u32[o + 3] = color
159
+ this.count++
160
+ }
161
+
162
+ // A right-sized COPY, not a subarray view — see the class comment. Skipped
163
+ // entirely when the seed turned out to be exact, which is the common path.
164
+ finish() {
165
+ const used = this.count * INSTANCE_STRIDE_BYTES
166
+ return used === this.buf.byteLength ? this.buf : this.buf.slice(0, used)
167
+ }
168
+ }
@@ -0,0 +1,22 @@
1
+ // The `span` mark shape: one instance is a coloured rectangle spanning
2
+ // [x, x2) in absolute genomic coords, on the row band given by the `row`
3
+ // channel. The geometry — row band, hp clip-x span, min-width widening — is
4
+ // `rowRect`'s; this is the entry point that makes it a pass a mark can draw.
5
+ //! targets: wgsl, glsl
6
+ //! instance-writer
7
+
8
+ import rowRect;
9
+
10
+ public static const uint VERTS_PER_INSTANCE = 6u;
11
+
12
+ [[vk::binding(1, 0)]] ConstantBuffer<RowRectUniforms> u;
13
+
14
+ [shader("vertex")]
15
+ RowRectVsOut vs_main(RowRectInstance inst, uint vid : SV_VertexID) {
16
+ return rowRectVertex(inst, vid, u);
17
+ }
18
+
19
+ [shader("fragment")]
20
+ float4 fs_main(RowRectVsOut v) : SV_Target {
21
+ return v.color;
22
+ }
@@ -1,90 +0,0 @@
1
- import { Canvas2DRenderingBackendBase, GpuRenderingBackendBase } from './renderingBackendBase.ts';
2
- import type { FrameDimensions, RenderingBackend } from './renderingBackendBase.ts';
3
- /**
4
- * Shared contract for monolithic GPU backends (HiC, LD, multi-variant
5
- * matrix) — displays with no region partitioning. One payload under the
6
- * `data` key, one draw per frame. Render receives the data directly so
7
- * Canvas2D backends stay stateless; GPU backends use `upload` to push bytes
8
- * into a HAL buffer and ignore the `data` arg at render time.
9
- *
10
- * A display with a second upload beside the payload — HiC's and LD's colour
11
- * ramp texture, derived from a setting rather than the fetch — widens `Key`
12
- * and `Cell` so `upload` takes both cells and tells them apart, while `render`
13
- * is still handed the payload alone.
14
- */
15
- export interface GlobalRenderingBackend<UploadData, RenderState, Key extends string = 'data', Cell = UploadData> extends RenderingBackend {
16
- upload(key: Key, data: Cell): void;
17
- release(key: Key): void;
18
- /**
19
- * Paint one frame and report whether real content reached the canvas — the
20
- * monolithic twin of `PerRegionRenderingBackend.renderBlocks`, and it answers
21
- * for the same reason. `RenderLifecycleMixin` flips `canvasDrawn` (the loading
22
- * scrim, `data-display-drawn`, every browser test's wait) only on `true`, so a
23
- * tick that drew nothing must say so.
24
- *
25
- * **This used to return `void`, and the answer was the display's to give.**
26
- * Each of the three wrote the same six lines — "data is here, so return
27
- * `true`" — beside a renderer whose own guard was strictly narrower: a color
28
- * ramp that had not arrived, a zero instance count, an empty HAL buffer. Two
29
- * expressions of one fact, and the one that could see what actually happened
30
- * was not the one being asked. LD's model even carried a comment explaining
31
- * the workaround, which is the usual sign that a signature is wrong rather
32
- * than a caller.
33
- */
34
- render(data: UploadData | null, state: RenderState): boolean;
35
- }
36
- /**
37
- * Canvas2D-side base for `GlobalRenderingBackend` implementations. Inherits the
38
- * `canvas` + 2D context (and no-op `dispose`) from
39
- * `Canvas2DRenderingBackendBase`; stubs `upload` (no-op — data flows through
40
- * `render`).
41
- *
42
- * `render` is concrete and runs `prepareCanvas` (DPR-aware backing-store sizing)
43
- * before handing off to the abstract `draw`, exactly as
44
- * `Canvas2DPerRegionRenderingBackend` does — so a renderer here can no more
45
- * forget it and paint blurry on hi-DPI than one over there can. All three
46
- * subclasses opened with that same call and none of them had to.
47
- *
48
- * A null payload still prepares (and so clears) the canvas, then skips the
49
- * draw: that is the frame where a refetch is in flight and the old picture must
50
- * not stay up.
51
- */
52
- export declare abstract class Canvas2DGlobalRenderingBackend<UploadData, RenderState extends FrameDimensions, Key extends string = 'data', Cell = UploadData> extends Canvas2DRenderingBackendBase implements GlobalRenderingBackend<UploadData, RenderState, Key, Cell> {
53
- upload(_key: Key, _cell: Cell): void;
54
- release(_key: Key): void;
55
- render(data: UploadData | null, state: RenderState): boolean;
56
- /**
57
- * Paint `data` into `this.ctx` (already DPR-prepared) and answer whether
58
- * anything reached it. Delegates to the plugin's pure `drawXxxBlocks`
59
- * function, the one SVG export shares.
60
- */
61
- protected abstract draw(data: UploadData, state: RenderState): boolean;
62
- }
63
- /**
64
- * GPU-side base for `GlobalRenderingBackend` implementations. Inherits the
65
- * `hal` reference, the pre-allocated uniform scratch buffer, and the
66
- * HAL-delegating `dispose` from `GpuRenderingBackendBase`. Subclasses implement
67
- * `upload` and `draw`.
68
- *
69
- * `render` is the frame scaffold the per-region base has always had and this
70
- * one did not: resize the backing store, `beginFrame`/`endFrame`, and hand the
71
- * middle to `draw`. **The pairing is the point.** `beginFrame` is what clears
72
- * the canvas, so a frame that draws nothing still has to open and close one or
73
- * the last picture stays up — and with each subclass writing its own scaffold,
74
- * that invariant held three times by hand.
75
- *
76
- * `release` is a no-op by default: a payload leaving the map is answered by
77
- * `render` being handed `null`, and `beginFrame` clears the last picture. A
78
- * subclass whose buffers are worth freeing early overrides it.
79
- */
80
- export declare abstract class GpuGlobalRenderingBackend<UploadData, RenderState extends FrameDimensions, Key extends string = 'data', Cell = UploadData> extends GpuRenderingBackendBase implements GlobalRenderingBackend<UploadData, RenderState, Key, Cell> {
81
- abstract upload(key: Key, data: Cell): void;
82
- release(): void;
83
- render(data: UploadData | null, state: RenderState): boolean;
84
- /**
85
- * Write uniforms and issue the draw passes for one frame, inside an already-
86
- * open `beginFrame`/`endFrame`. Return whether anything was drawn — a buffer
87
- * the upload has not filled yet is the ordinary reason not to.
88
- */
89
- protected abstract draw(data: UploadData, state: RenderState): boolean;
90
- }