@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.
Files changed (174) hide show
  1. package/esm/RenderCanvas.d.ts +2 -2
  2. package/esm/RenderLifecycleMixin.d.ts +7 -6
  3. package/esm/RenderLifecycleMixin.js +7 -6
  4. package/esm/canvas2dUtils.d.ts +58 -17
  5. package/esm/canvas2dUtils.js +82 -30
  6. package/esm/computePipeline.d.ts +4 -3
  7. package/esm/computePipeline.js +21 -14
  8. package/esm/coverageBand.d.ts +3 -3
  9. package/esm/coverageBand.js +2 -2
  10. package/esm/coverageBandBuffers.d.ts +2 -3
  11. package/esm/createRenderingBackend.d.ts +1 -1
  12. package/esm/displayPhase.d.ts +79 -109
  13. package/esm/displayPhase.js +42 -83
  14. package/esm/gpuDevice.d.ts +11 -10
  15. package/esm/gpuDevice.js +10 -11
  16. package/esm/graphicsCapabilities.d.ts +6 -7
  17. package/esm/graphicsCapabilities.js +3 -4
  18. package/esm/hal/createHal.js +1 -1
  19. package/esm/hal/deviceGpuCache.d.ts +25 -30
  20. package/esm/hal/deviceGpuCache.js +85 -97
  21. package/esm/hal/index.d.ts +2 -2
  22. package/esm/hal/index.js +1 -1
  23. package/esm/hal/mockHal.d.ts +8 -6
  24. package/esm/hal/mockHal.js +7 -2
  25. package/esm/hal/passIds.d.ts +8 -7
  26. package/esm/hal/passIds.js +11 -10
  27. package/esm/hal/types.d.ts +28 -12
  28. package/esm/hal/webgl2Hal.d.ts +11 -3
  29. package/esm/hal/webgl2Hal.js +116 -145
  30. package/esm/hal/webgpuHal.d.ts +8 -8
  31. package/esm/hal/webgpuHal.js +97 -124
  32. package/esm/instancePass.d.ts +2 -1
  33. package/esm/mapUploadSync.d.ts +2 -2
  34. package/esm/mapUploadSync.js +2 -2
  35. package/esm/marks/barMark.d.ts +6 -3
  36. package/esm/marks/barMark.js +19 -13
  37. package/esm/marks/drawAgainstHit.d.ts +58 -39
  38. package/esm/marks/drawAgainstHit.js +136 -96
  39. package/esm/marks/glyphPaint.d.ts +14 -2
  40. package/esm/marks/glyphPaint.js +16 -2
  41. package/esm/marks/index.d.ts +4 -2
  42. package/esm/marks/index.js +3 -2
  43. package/esm/marks/markBackend.d.ts +2 -2
  44. package/esm/marks/markBackend.js +2 -2
  45. package/esm/marks/markHit.d.ts +2 -2
  46. package/esm/marks/markHit.js +2 -2
  47. package/esm/marks/markPlan.d.ts +2 -2
  48. package/esm/marks/markPlan.js +2 -2
  49. package/esm/marks/markRamp.d.ts +1 -3
  50. package/esm/marks/markRamp.js +1 -5
  51. package/esm/marks/nearestMarkHit.d.ts +49 -0
  52. package/esm/marks/nearestMarkHit.js +126 -0
  53. package/esm/marks/pointMark.d.ts +9 -2
  54. package/esm/marks/pointMark.js +36 -31
  55. package/esm/marks/rowLane.d.ts +17 -0
  56. package/esm/marks/rowLane.js +11 -0
  57. package/esm/marks/spanMark.d.ts +6 -26
  58. package/esm/marks/spanMark.js +34 -31
  59. package/esm/marks/types.d.ts +51 -181
  60. package/esm/marks/types.js +91 -131
  61. package/esm/namedReactions.d.ts +3 -4
  62. package/esm/namedReactions.js +3 -4
  63. package/esm/scoreScale.d.ts +20 -0
  64. package/esm/scoreScale.js +77 -0
  65. package/esm/shaders/barMark.generated.d.ts +2 -3
  66. package/esm/shaders/barMark.generated.js +4 -3
  67. package/esm/shaders/barMark.glsl.generated.d.ts +2 -0
  68. package/esm/shaders/barMark.glsl.generated.js +4 -0
  69. package/esm/shaders/barMark.iface.generated.d.ts +12 -6
  70. package/esm/shaders/barMark.iface.generated.js +27 -15
  71. package/esm/shaders/barMark.wgsl.generated.d.ts +1 -0
  72. package/esm/shaders/barMark.wgsl.generated.js +3 -0
  73. package/esm/shaders/coverageBar.generated.d.ts +2 -3
  74. package/esm/shaders/coverageBar.generated.js +4 -3
  75. package/esm/shaders/coverageBar.glsl.generated.d.ts +2 -0
  76. package/esm/shaders/coverageBar.glsl.generated.js +4 -0
  77. package/esm/shaders/coverageBar.iface.generated.js +1 -1
  78. package/esm/shaders/coverageBar.wgsl.generated.d.ts +1 -0
  79. package/esm/shaders/coverageBar.wgsl.generated.js +3 -0
  80. package/esm/shaders/coverageIndicator.generated.d.ts +2 -3
  81. package/esm/shaders/coverageIndicator.generated.js +4 -3
  82. package/esm/shaders/coverageIndicator.glsl.generated.d.ts +2 -0
  83. package/esm/shaders/coverageIndicator.glsl.generated.js +4 -0
  84. package/esm/shaders/coverageIndicator.iface.generated.js +1 -1
  85. package/esm/shaders/coverageIndicator.wgsl.generated.d.ts +1 -0
  86. package/esm/shaders/coverageIndicator.wgsl.generated.js +3 -0
  87. package/esm/shaders/coverageInterbase.generated.d.ts +2 -3
  88. package/esm/shaders/coverageInterbase.generated.js +4 -3
  89. package/esm/shaders/coverageInterbase.glsl.generated.d.ts +2 -0
  90. package/esm/shaders/coverageInterbase.glsl.generated.js +4 -0
  91. package/esm/shaders/coverageInterbase.iface.generated.js +1 -1
  92. package/esm/shaders/coverageInterbase.wgsl.generated.d.ts +1 -0
  93. package/esm/shaders/coverageInterbase.wgsl.generated.js +3 -0
  94. package/esm/shaders/coverageMod.generated.d.ts +2 -3
  95. package/esm/shaders/coverageMod.generated.js +4 -3
  96. package/esm/shaders/coverageMod.glsl.generated.d.ts +2 -0
  97. package/esm/shaders/coverageMod.glsl.generated.js +4 -0
  98. package/esm/shaders/coverageMod.iface.generated.js +1 -1
  99. package/esm/shaders/coverageMod.wgsl.generated.d.ts +1 -0
  100. package/esm/shaders/coverageMod.wgsl.generated.js +3 -0
  101. package/esm/shaders/coverageSnp.generated.d.ts +2 -3
  102. package/esm/shaders/coverageSnp.generated.js +4 -3
  103. package/esm/shaders/coverageSnp.glsl.generated.d.ts +2 -0
  104. package/esm/shaders/coverageSnp.glsl.generated.js +4 -0
  105. package/esm/shaders/coverageSnp.iface.generated.js +1 -1
  106. package/esm/shaders/coverageSnp.wgsl.generated.d.ts +1 -0
  107. package/esm/shaders/coverageSnp.wgsl.generated.js +3 -0
  108. package/esm/shaders/pointMark.generated.d.ts +2 -3
  109. package/esm/shaders/pointMark.generated.js +4 -3
  110. package/esm/shaders/pointMark.glsl.generated.d.ts +2 -0
  111. package/esm/shaders/pointMark.glsl.generated.js +4 -0
  112. package/esm/shaders/pointMark.iface.generated.d.ts +11 -3
  113. package/esm/shaders/pointMark.iface.generated.js +23 -9
  114. package/esm/shaders/pointMark.js.generated.d.ts +1 -1
  115. package/esm/shaders/pointMark.js.generated.js +51 -29
  116. package/esm/shaders/pointMark.wgsl.generated.d.ts +1 -0
  117. package/esm/shaders/pointMark.wgsl.generated.js +3 -0
  118. package/esm/shaders/scoreScale.js.generated.js +46 -22
  119. package/esm/shaders/spanMark.generated.d.ts +2 -3
  120. package/esm/shaders/spanMark.generated.js +4 -3
  121. package/esm/shaders/spanMark.glsl.generated.d.ts +2 -0
  122. package/esm/shaders/spanMark.glsl.generated.js +4 -0
  123. package/esm/shaders/spanMark.iface.generated.js +1 -1
  124. package/esm/shaders/spanMark.wgsl.generated.d.ts +1 -0
  125. package/esm/shaders/spanMark.wgsl.generated.js +3 -0
  126. package/esm/slangPass.d.ts +9 -10
  127. package/esm/slangPass.js +9 -3
  128. package/esm/useGraphicsCapabilities.d.ts +1 -1
  129. package/esm/useGraphicsCapabilities.js +1 -1
  130. package/esm/webgpuUtils.d.ts +3 -11
  131. package/esm/webgpuUtils.js +12 -33
  132. package/package.json +3 -2
  133. package/src/shaders/barMark.generated.ts +6 -5
  134. package/src/shaders/barMark.glsl.generated.ts +6 -0
  135. package/src/shaders/barMark.iface.generated.ts +31 -15
  136. package/src/shaders/barMark.slang +11 -3
  137. package/src/shaders/barMark.wgsl.generated.ts +4 -0
  138. package/src/shaders/colorPack.slang +3 -2
  139. package/src/shaders/coverageBand.slang +5 -11
  140. package/src/shaders/coverageBar.generated.ts +6 -5
  141. package/src/shaders/coverageBar.glsl.generated.ts +6 -0
  142. package/src/shaders/coverageBar.iface.generated.ts +1 -1
  143. package/src/shaders/coverageBar.slang +1 -1
  144. package/src/shaders/coverageBar.wgsl.generated.ts +4 -0
  145. package/src/shaders/coverageIndicator.generated.ts +6 -5
  146. package/src/shaders/coverageIndicator.glsl.generated.ts +6 -0
  147. package/src/shaders/coverageIndicator.iface.generated.ts +1 -1
  148. package/src/shaders/coverageIndicator.wgsl.generated.ts +4 -0
  149. package/src/shaders/coverageInterbase.generated.ts +6 -5
  150. package/src/shaders/coverageInterbase.glsl.generated.ts +6 -0
  151. package/src/shaders/coverageInterbase.iface.generated.ts +1 -1
  152. package/src/shaders/coverageInterbase.wgsl.generated.ts +4 -0
  153. package/src/shaders/coverageMod.generated.ts +6 -5
  154. package/src/shaders/coverageMod.glsl.generated.ts +6 -0
  155. package/src/shaders/coverageMod.iface.generated.ts +1 -1
  156. package/src/shaders/coverageMod.wgsl.generated.ts +4 -0
  157. package/src/shaders/coverageSnp.generated.ts +6 -5
  158. package/src/shaders/coverageSnp.glsl.generated.ts +6 -0
  159. package/src/shaders/coverageSnp.iface.generated.ts +1 -1
  160. package/src/shaders/coverageSnp.wgsl.generated.ts +4 -0
  161. package/src/shaders/hpmath.slang +18 -6
  162. package/src/shaders/pointMark.generated.ts +6 -6
  163. package/src/shaders/pointMark.glsl.generated.ts +6 -0
  164. package/src/shaders/pointMark.iface.generated.ts +28 -9
  165. package/src/shaders/pointMark.js.generated.ts +48 -29
  166. package/src/shaders/pointMark.slang +18 -5
  167. package/src/shaders/pointMark.wgsl.generated.ts +4 -0
  168. package/src/shaders/scoreScale.js.generated.ts +43 -22
  169. package/src/shaders/scoreScale.slang +37 -53
  170. package/src/shaders/spanMark.generated.ts +6 -5
  171. package/src/shaders/spanMark.glsl.generated.ts +6 -0
  172. package/src/shaders/spanMark.iface.generated.ts +1 -1
  173. package/src/shaders/spanMark.wgsl.generated.ts +4 -0
  174. package/src/shaders/valueScale.slang +8 -45
@@ -1,16 +1,20 @@
1
1
  /**
2
2
  * The mutually-exclusive visual state of a GPU display. Computing it in one
3
- * place means the precedence — renderError > tooLarge > error > loading > ready
4
- * — lives in a single function instead of being re-encoded by subtraction
5
- * (`&& !regionTooLarge && !error && !renderError`) in every display model AND
6
- * mirrored a second time by `DisplayChrome`'s JSX order. `DisplayChrome`
7
- * switches on it; the loading scrim's visibility is just
8
- * `displayPhase === 'loading'`, computed once in the chrome.
3
+ * place means the precedence — renderError > tooLarge > error > canceled >
4
+ * loading > ready — lives in a single function instead of being re-encoded by
5
+ * subtraction (`&& !regionTooLarge && !error && !renderError`) in every display
6
+ * model AND mirrored a second time by `DisplayChrome`'s JSX order.
9
7
  *
10
8
  * `renderError` and `tooLarge` replace the whole display subtree (their own
11
9
  * root element, so the transition unmounts the canvas → `canvasRef(null)` →
12
- * `backend.dispose()`). `error` and `loading` are overlays rendered *over* the
13
- * still-mounted canvas, so they share the `ready` branch's root.
10
+ * `backend.dispose()`). `error`, `canceled` and `loading` are overlays rendered
11
+ * *over* the still-mounted canvas, so they share the `ready` branch's root.
12
+ *
13
+ * `loading` is the one phase that means work is outstanding: every reader
14
+ * asking "is it finished" (`AppReadyMarker`, `jb.waitReady`, the capture waits)
15
+ * treats every other phase as finished. `canceled` is the user's standing stop,
16
+ * durable until Retry (or, on the LGV families, a viewport change); the chrome
17
+ * draws it as the loading overlay in its canceled state, which carries Retry.
14
18
  */
15
19
  export type DisplayPhase = 'renderError' | DisplayStatusPhase;
16
20
  /**
@@ -20,10 +24,14 @@ export type DisplayPhase = 'renderError' | DisplayStatusPhase;
20
24
  * (the backend hook's `retry()`), which is exactly the line between the shared
21
25
  * status chrome and the GPU chrome wrapped around it: `DisplayStatusChrome`
22
26
  * takes this, `DisplayChrome` takes the wider union and peels off `renderError`
23
- * before delegating. A main-thread SVG display (arc) computes this one and is
24
- * then *unable* to claim a phase whose banner has no retry to offer.
27
+ * before delegating.
28
+ */
29
+ export type DisplayStatusPhase = 'tooLarge' | 'error' | DisplayActivityPhase;
30
+ /**
31
+ * What is left once no failure terminal holds, as `computeActivityPhase`
32
+ * answers it for every fetching family.
25
33
  */
26
- export type DisplayStatusPhase = 'tooLarge' | 'error' | 'loading' | 'ready';
34
+ export type DisplayActivityPhase = 'canceled' | 'loading' | 'ready';
27
35
  export interface DisplayStatusPhaseInputs {
28
36
  regionTooLarge: boolean;
29
37
  error: unknown;
@@ -32,133 +40,95 @@ export interface DisplayPhaseInputs extends DisplayStatusPhaseInputs {
32
40
  renderError: unknown;
33
41
  }
34
42
  /**
35
- * `loading` is the only display-specific axis: MultiRegion passes
36
- * "!isReady || stale viewport", Global passes "fetch in flight", sequence
37
- * layers a zoom gate on top. The terminal precedence above it is identical for
38
- * every display, so it lives here — pass the display's loading condition and
39
- * get the resolved phase back.
43
+ * The terminal precedence shared by every display, over the display's own
44
+ * activity phase.
40
45
  *
41
- * `loading` is a **thunk**, evaluated only after the three terminal states are
42
- * ruled out. This is load-bearing for MobX, not a micro-optimization: the
43
- * loading condition typically reads the containing view (`visibleRegions`,
44
- * `loadedRegions`, …), a large reactive dependency set. Evaluating it eagerly
45
- * would make every reader of `displayPhase` subscribe to all of it even while a
46
- * banner is up — and `DisplayChrome`'s observer, re-firing on that churn during
47
- * a terminal state, then fails to commit the banner subtree to the DOM (the
48
- * React-reconciliation hazard documented in DisplayChrome.tsx). Short-circuiting
49
- * keeps the tracked set to just the terminal flags when one is active, matching
50
- * the old direct-read early-returns.
46
+ * `activity` is a **thunk**, evaluated only after the three failure terminals
47
+ * are ruled out. Load-bearing for MobX: the activity condition typically reads
48
+ * the containing view (`visibleRegions`, `loadedRegions`, …), and evaluating it
49
+ * eagerly would subscribe every reader of `displayPhase` to that churn even
50
+ * while a banner is up — and `DisplayChrome`'s observer, re-firing during a
51
+ * terminal state, then fails to commit the banner subtree (see
52
+ * DisplayChrome.tsx).
51
53
  */
52
- export declare function computeDisplayPhase({ renderError, regionTooLarge, error }: DisplayPhaseInputs, loading: () => boolean): DisplayPhase;
54
+ export declare function computeDisplayPhase({ renderError, regionTooLarge, error }: DisplayPhaseInputs, activity: () => DisplayActivityPhase): DisplayPhase;
53
55
  /**
54
- * The five terms the `loading` condition is built from, minus the one that is a
55
- * thunk. All cheap flags on the display itself, so they are read eagerly the
56
- * same way `computeDisplayPhase` reads its terminals.
56
+ * The terms the activity phase is built from, minus the thunks. All cheap flags
57
+ * on the display itself, so they are read eagerly the same way
58
+ * `computeDisplayPhase` reads its terminals.
57
59
  */
58
- export interface DisplayLoadingInputs {
60
+ export interface DisplayActivityInputs {
61
+ /**
62
+ * The containing track is minimized. Every fetch gate skips a minimized
63
+ * track and its canvas is unmounted, so nothing the terms below wait on can
64
+ * arrive. Suppresses them all, like `fetchInert`, which a subclass overrides
65
+ * outright and so cannot carry it.
66
+ */
67
+ isMinimized: boolean;
59
68
  /**
60
69
  * The display is deliberately showing a static message instead of data
61
- * (sequence past base resolution, LD with the triangle off), so no scrim at
62
- * all. Suppresses every term below — which is why `rendersCanvas: false` is
63
- * not a substitute: that one drops the pre-first-paint term alone, leaving the
64
- * fetch terms free to scrim over the placeholder, and `isLoadingOrCanceled`
65
- * includes a cancel that is deliberately durable.
70
+ * (sequence past base resolution, LD with the triangle off). Suppresses every
71
+ * term below, a standing cancel included — which is why `rendersCanvas: false`
72
+ * is not a substitute: that one drops the pre-first-paint term alone.
66
73
  */
67
74
  fetchInert: boolean;
68
75
  /**
69
- * The view holds no content block, so there is nothing to fetch and nothing
70
- * to paint: `showAllRegions` over a region set whose every member falls under
71
- * `minimumBlockWidth`, which is a scaffold-level assembly and little else (see
72
- * `viewportEmpty`). Suppresses every term below for the same reason
73
- * `fetchInert` does, and it is the pre-first-paint one that makes it
74
- * necessary: no fetch is issued, so `canvasDrawn` is never set and the scrim
75
- * would sit there for as long as the viewport stays off content.
76
+ * The view holds no content block (`showAllRegions` over a region set whose
77
+ * every member falls under `minimumBlockWidth`; see `viewportEmpty`), so no
78
+ * fetch is issued and `canvasDrawn` is never set. Suppresses every term below.
76
79
  */
77
80
  viewportEmpty: boolean;
81
+ /** a fetch is in flight */
82
+ isLoading: boolean;
78
83
  /**
79
- * A fetch is in flight, or the user canceled one. **Never a bare
80
- * `isLoading`**: cancel drops the stop token synchronously, so without the
81
- * second term the phase falls to `ready` and the overlay carrying the Retry
82
- * button unmounts, leaving the display stopped and empty with nothing to
83
- * click.
84
+ * The user canceled a fetch and has not retried. Outranks every loading term,
85
+ * since what those wait on is the fetch the user stopped. Kept apart from
86
+ * `isLoading` because a cancel is finished, not pending: folded into the
87
+ * loading term, a canceled display read `loading` until Retry and parked every
88
+ * readiness gate on it.
84
89
  */
85
- isLoadingOrCanceled: boolean;
90
+ fetchCanceled: boolean;
86
91
  /**
87
92
  * A load this display depends on beyond its primary fetch has not landed for
88
- * the first time, so the frame the primary fetch calls current is still
89
- * missing something (multi-way synteny's lane genes and links). A loading
90
- * term rather than a `dataSuperseded` fold: that flag holds the export
91
- * through every later refetch too, and a scrim over lanes that are already
92
- * drawn is the thing a display saying this wants to avoid.
93
+ * the first time (multi-way synteny's lane genes and links). A loading term
94
+ * rather than a `dataSuperseded` fold: that flag holds the export through
95
+ * every later refetch too, and a scrim over lanes already drawn is the thing
96
+ * a display saying this wants to avoid.
93
97
  */
94
98
  awaitingDependentData: boolean;
95
99
  /**
96
100
  * Whether this display paints a canvas in its current configuration. Gates
97
- * the pre-first-paint term alone, because a display that is showing a
98
- * deliberate non-canvas placeholder (LD with the triangle off) never flips
99
- * `canvasDrawn` and would sit under the scrim forever.
101
+ * the pre-first-paint term alone, because a display showing a deliberate
102
+ * non-canvas placeholder (LD with the triangle off) never flips `canvasDrawn`.
100
103
  */
101
104
  rendersCanvas: boolean;
102
105
  /** first paint has happened */
103
106
  canvasDrawn: boolean;
104
107
  }
105
108
  /**
106
- * The `loading` term itself — the one axis `computeDisplayPhase` leaves to its
107
- * caller — expressed once for both LGV display families.
109
+ * The activity phase, expressed once for every fetching display family, so a
110
+ * term added here reaches every display. The two LGV families hand-wrote it
111
+ * until they had drifted on the cancel term and on which suppression hook each
112
+ * carried.
108
113
  *
109
- * It was two hand-written expressions, and they had already drifted apart in
110
- * three ways that were equivalent only by accident: the per-region family spelled
111
- * the cancel term out by hand (`!isReady || … || fetchCanceled`, over a bare
112
- * `isLoading`) rather than reading `isLoadingOrCanceled`, and each family carried
113
- * exactly one of the two suppression hooks — `fetchInert` on per-region,
114
- * `rendersCanvas` on global — so a display in the other family could express its
115
- * case only by overriding `displayPhase`, which is the thing both hooks exist to
116
- * prevent. Single-sourcing the expression is the same argument the precedence
117
- * above already won, one level down: a term added here reaches every display,
118
- * instead of reaching whichever family the author happened to be reading.
119
- *
120
- * The four inputs all now live on a mixin every foundation composes —
121
- * `fetchInert` / `isLoadingOrCanceled` on `FetchMixin`, `rendersCanvas` /
122
- * `canvasDrawn` on `RenderLifecycleMixin` — so `foundationDisplayPhase` passes
123
- * the model straight through and the only thing a family supplies is
124
- * `viewportCurrent`: per-region its staleness predicate, global `() => true` (it
125
- * keeps the last frame up through a refetch rather than scrimming). Two hooks
126
- * used to be hard-coded per family instead, which is how the second drift
127
- * happened after the first was fixed — LD needed `fetchInert` and the
128
- * global family did not have it.
129
- *
130
- * `viewportCurrent` is a **thunk** for the same MobX reason `computeDisplayPhase`'s
131
- * `loading` is: it reads the containing view (`visibleRegions`, `loadedRegions`),
132
- * while the flag inputs above are on the display itself. Short-circuiting keeps a
133
- * suppressed or already-loading display from subscribing to viewport churn.
114
+ * `viewportCurrent` is a **thunk** for the MobX reason `computeDisplayPhase`'s
115
+ * `activity` is: it reads the containing view, while the flags above are on the
116
+ * display. Per-region passes its staleness predicate, global `() => true` (it
117
+ * keeps the last frame up through a refetch rather than scrimming).
134
118
  *
135
119
  * `hostMounted` is a thunk for the same reason, and gates the **pre-first-paint
136
- * term only**. A container that mounts a view's body lazily — `ViewContainer`
137
- * does, to stay under the WebGL2 context ceiling — leaves an off-screen view
138
- * with no canvas, so `markCanvasDrawn` is never called and `!canvasDrawn` stays
139
- * true with nothing able to resolve it. That parked `[data-app-phase="ready"]`
140
- * for the whole app on a view the user could not see, and every capture and e2e
141
- * gate waiting on it burned its full timeout.
142
- *
143
- * It is a required flag on every view and on the duck-typed host contracts, not
144
- * an optional one defaulted here: a stand-in that forgot it would silently
145
- * excuse the first paint and let a readiness gate fire over a view that had not
146
- * started.
147
- *
148
- * The fetch terms deliberately stay live while unmounted: an off-screen display
149
- * still fetches, so reporting it idle would let a readiness gate fire over work
150
- * genuinely in flight — and during cold load `visible` is false until the
151
- * IntersectionObserver's first callback, which is exactly when a display must
152
- * not flash `ready`. Only the paint it will never do is excused.
120
+ * term only**. `ViewContainer` mounts an off-screen view's body lazily, so its
121
+ * display has no canvas and `!canvasDrawn` could never resolve — which parked
122
+ * `[data-app-phase="ready"]` for the whole app. It is required on every view and
123
+ * host contract rather than defaulted there, so a stand-in has to state it and
124
+ * cannot excuse the first paint by omission. The fetch terms stay live while unmounted: an
125
+ * off-screen display still fetches, and during cold load `visible` is false
126
+ * until the IntersectionObserver's first callback.
153
127
  */
154
- export declare function computeLoadingTerm({ fetchInert, viewportEmpty, isLoadingOrCanceled, awaitingDependentData, rendersCanvas, canvasDrawn, }: DisplayLoadingInputs, viewportCurrent: () => boolean, hostMounted?: () => boolean): boolean;
128
+ export declare function computeActivityPhase({ isMinimized, fetchInert, viewportEmpty, isLoading, fetchCanceled, awaitingDependentData, rendersCanvas, canvasDrawn, }: DisplayActivityInputs, viewportCurrent: () => boolean, hostMounted?: () => boolean): DisplayActivityPhase;
155
129
  /**
156
130
  * The same ranking minus `renderError`, for a display with no rendering backend
157
- * to fail (arc's main-thread SVG). `computeDisplayPhase` delegates here rather
158
- * than restating the tail, so there is still exactly one place the order lives —
159
- * and the narrower return type is what lets `DisplayStatusChrome` accept a
160
- * backend-less display without either a cast or a dead branch.
161
- *
162
- * `loading` is a thunk here for the same reason as above.
131
+ * to fail (arc's main-thread SVG). `computeDisplayPhase` delegates here, so the
132
+ * order lives in one place.
163
133
  */
164
- export declare function computeDisplayStatusPhase({ regionTooLarge, error }: DisplayStatusPhaseInputs, loading: () => boolean): DisplayStatusPhase;
134
+ export declare function computeDisplayStatusPhase({ regionTooLarge, error }: DisplayStatusPhaseInputs, activity: () => DisplayActivityPhase): DisplayStatusPhase;
@@ -1,98 +1,57 @@
1
1
  /**
2
- * `loading` is the only display-specific axis: MultiRegion passes
3
- * "!isReady || stale viewport", Global passes "fetch in flight", sequence
4
- * layers a zoom gate on top. The terminal precedence above it is identical for
5
- * every display, so it lives here — pass the display's loading condition and
6
- * get the resolved phase back.
7
- *
8
- * `loading` is a **thunk**, evaluated only after the three terminal states are
9
- * ruled out. This is load-bearing for MobX, not a micro-optimization: the
10
- * loading condition typically reads the containing view (`visibleRegions`,
11
- * `loadedRegions`, …), a large reactive dependency set. Evaluating it eagerly
12
- * would make every reader of `displayPhase` subscribe to all of it even while a
13
- * banner is up — and `DisplayChrome`'s observer, re-firing on that churn during
14
- * a terminal state, then fails to commit the banner subtree to the DOM (the
15
- * React-reconciliation hazard documented in DisplayChrome.tsx). Short-circuiting
16
- * keeps the tracked set to just the terminal flags when one is active, matching
17
- * the old direct-read early-returns.
2
+ * The terminal precedence shared by every display, over the display's own
3
+ * activity phase.
4
+ *
5
+ * `activity` is a **thunk**, evaluated only after the three failure terminals
6
+ * are ruled out. Load-bearing for MobX: the activity condition typically reads
7
+ * the containing view (`visibleRegions`, `loadedRegions`, …), and evaluating it
8
+ * eagerly would subscribe every reader of `displayPhase` to that churn even
9
+ * while a banner is up — and `DisplayChrome`'s observer, re-firing during a
10
+ * terminal state, then fails to commit the banner subtree (see
11
+ * DisplayChrome.tsx).
18
12
  */
19
- export function computeDisplayPhase({ renderError, regionTooLarge, error }, loading) {
13
+ export function computeDisplayPhase({ renderError, regionTooLarge, error }, activity) {
20
14
  return renderError
21
15
  ? 'renderError'
22
- : computeDisplayStatusPhase({ regionTooLarge, error }, loading);
16
+ : computeDisplayStatusPhase({ regionTooLarge, error }, activity);
23
17
  }
24
18
  /**
25
- * The `loading` term itself — the one axis `computeDisplayPhase` leaves to its
26
- * caller — expressed once for both LGV display families.
27
- *
28
- * It was two hand-written expressions, and they had already drifted apart in
29
- * three ways that were equivalent only by accident: the per-region family spelled
30
- * the cancel term out by hand (`!isReady || … || fetchCanceled`, over a bare
31
- * `isLoading`) rather than reading `isLoadingOrCanceled`, and each family carried
32
- * exactly one of the two suppression hooks — `fetchInert` on per-region,
33
- * `rendersCanvas` on global — so a display in the other family could express its
34
- * case only by overriding `displayPhase`, which is the thing both hooks exist to
35
- * prevent. Single-sourcing the expression is the same argument the precedence
36
- * above already won, one level down: a term added here reaches every display,
37
- * instead of reaching whichever family the author happened to be reading.
38
- *
39
- * The four inputs all now live on a mixin every foundation composes —
40
- * `fetchInert` / `isLoadingOrCanceled` on `FetchMixin`, `rendersCanvas` /
41
- * `canvasDrawn` on `RenderLifecycleMixin` — so `foundationDisplayPhase` passes
42
- * the model straight through and the only thing a family supplies is
43
- * `viewportCurrent`: per-region its staleness predicate, global `() => true` (it
44
- * keeps the last frame up through a refetch rather than scrimming). Two hooks
45
- * used to be hard-coded per family instead, which is how the second drift
46
- * happened after the first was fixed — LD needed `fetchInert` and the
47
- * global family did not have it.
19
+ * The activity phase, expressed once for every fetching display family, so a
20
+ * term added here reaches every display. The two LGV families hand-wrote it
21
+ * until they had drifted on the cancel term and on which suppression hook each
22
+ * carried.
48
23
  *
49
- * `viewportCurrent` is a **thunk** for the same MobX reason `computeDisplayPhase`'s
50
- * `loading` is: it reads the containing view (`visibleRegions`, `loadedRegions`),
51
- * while the flag inputs above are on the display itself. Short-circuiting keeps a
52
- * suppressed or already-loading display from subscribing to viewport churn.
24
+ * `viewportCurrent` is a **thunk** for the MobX reason `computeDisplayPhase`'s
25
+ * `activity` is: it reads the containing view, while the flags above are on the
26
+ * display. Per-region passes its staleness predicate, global `() => true` (it
27
+ * keeps the last frame up through a refetch rather than scrimming).
53
28
  *
54
29
  * `hostMounted` is a thunk for the same reason, and gates the **pre-first-paint
55
- * term only**. A container that mounts a view's body lazily — `ViewContainer`
56
- * does, to stay under the WebGL2 context ceiling — leaves an off-screen view
57
- * with no canvas, so `markCanvasDrawn` is never called and `!canvasDrawn` stays
58
- * true with nothing able to resolve it. That parked `[data-app-phase="ready"]`
59
- * for the whole app on a view the user could not see, and every capture and e2e
60
- * gate waiting on it burned its full timeout.
61
- *
62
- * It is a required flag on every view and on the duck-typed host contracts, not
63
- * an optional one defaulted here: a stand-in that forgot it would silently
64
- * excuse the first paint and let a readiness gate fire over a view that had not
65
- * started.
66
- *
67
- * The fetch terms deliberately stay live while unmounted: an off-screen display
68
- * still fetches, so reporting it idle would let a readiness gate fire over work
69
- * genuinely in flight — and during cold load `visible` is false until the
70
- * IntersectionObserver's first callback, which is exactly when a display must
71
- * not flash `ready`. Only the paint it will never do is excused.
30
+ * term only**. `ViewContainer` mounts an off-screen view's body lazily, so its
31
+ * display has no canvas and `!canvasDrawn` could never resolve — which parked
32
+ * `[data-app-phase="ready"]` for the whole app. It is required on every view and
33
+ * host contract rather than defaulted there, so a stand-in has to state it and
34
+ * cannot excuse the first paint by omission. The fetch terms stay live while unmounted: an
35
+ * off-screen display still fetches, and during cold load `visible` is false
36
+ * until the IntersectionObserver's first callback.
72
37
  */
73
- export function computeLoadingTerm({ fetchInert, viewportEmpty, isLoadingOrCanceled, awaitingDependentData, rendersCanvas, canvasDrawn, }, viewportCurrent, hostMounted = () => true) {
74
- return (!fetchInert &&
75
- !viewportEmpty &&
76
- (isLoadingOrCanceled ||
77
- awaitingDependentData ||
78
- (rendersCanvas && hostMounted() && !canvasDrawn) ||
79
- !viewportCurrent()));
38
+ export function computeActivityPhase({ isMinimized, fetchInert, viewportEmpty, isLoading, fetchCanceled, awaitingDependentData, rendersCanvas, canvasDrawn, }, viewportCurrent, hostMounted = () => true) {
39
+ return isMinimized || fetchInert || viewportEmpty
40
+ ? 'ready'
41
+ : fetchCanceled
42
+ ? 'canceled'
43
+ : isLoading ||
44
+ awaitingDependentData ||
45
+ (rendersCanvas && hostMounted() && !canvasDrawn) ||
46
+ !viewportCurrent()
47
+ ? 'loading'
48
+ : 'ready';
80
49
  }
81
50
  /**
82
51
  * The same ranking minus `renderError`, for a display with no rendering backend
83
- * to fail (arc's main-thread SVG). `computeDisplayPhase` delegates here rather
84
- * than restating the tail, so there is still exactly one place the order lives —
85
- * and the narrower return type is what lets `DisplayStatusChrome` accept a
86
- * backend-less display without either a cast or a dead branch.
87
- *
88
- * `loading` is a thunk here for the same reason as above.
52
+ * to fail (arc's main-thread SVG). `computeDisplayPhase` delegates here, so the
53
+ * order lives in one place.
89
54
  */
90
- export function computeDisplayStatusPhase({ regionTooLarge, error }, loading) {
91
- return regionTooLarge
92
- ? 'tooLarge'
93
- : error
94
- ? 'error'
95
- : loading()
96
- ? 'loading'
97
- : 'ready';
55
+ export function computeDisplayStatusPhase({ regionTooLarge, error }, activity) {
56
+ return regionTooLarge ? 'tooLarge' : error ? 'error' : activity();
98
57
  }
@@ -5,7 +5,8 @@ interface GpuDeviceCell {
5
5
  devicePromise: Promise<GPUDevice | null> | null;
6
6
  /**
7
7
  * Proof that this machine's WebGPU works, set the first time a device is
8
- * acquired. It is what makes a failed acquisition readable, because the two
8
+ * acquired. The flag tells the two causes of a failed acquisition apart,
9
+ * because the two
9
10
  * causes are indistinguishable at the call site and want opposite handling:
10
11
  *
11
12
  * - Before it, failure means "no WebGPU here" — the ordinary path on most
@@ -16,9 +17,9 @@ interface GpuDeviceCell {
16
17
  * `requestAdapter` still declines. So this is not a rare race — it is the
17
18
  * expected timing of the one path that re-acquires.
18
19
  *
19
- * Caching the second kind is what `createGpuHal` then reads as "no WebGPU on
20
- * this machine" (its own words), silently pinning the whole page to WebGL2
21
- * until a reload. So past this flag a failure is retried and never cached.
20
+ * `createGpuHal` reads a cached failure of the second kind as "no WebGPU on
21
+ * this machine" (its own words) and pins the whole page to WebGL2 until a
22
+ * reload. So past this flag a failure is retried and never cached.
22
23
  */
23
24
  hadDevice: boolean;
24
25
  deviceLostListeners: Set<() => void>;
@@ -46,10 +47,10 @@ export declare function onDeviceLost(listener: () => void): () => void;
46
47
  * The renderers `?renderer=` can pin the page to. `canvas` is a spelling of
47
48
  * `canvas2d` kept because it is in the wild.
48
49
  *
49
- * A pin means *only* that rung — `createGpuHal` does not fall past it. That is
50
- * the whole point of the flag: it exists to compare backends, and a comparison
51
- * whose subject silently substitutes itself is worse than no comparison. The
52
- * failure is visible instead, as a `renderError` carrying the pin that could
50
+ * A pin means *only* that rung — `createGpuHal` does not fall past it. The flag
51
+ * is for comparing backends, and a comparison that renders with a different
52
+ * backend than the one pinned is worse than no comparison. The failure shows
53
+ * instead, as a `renderError` carrying the pin that could
53
54
  * not be honored, with the banner's "disable GPU" as the way out.
54
55
  */
55
56
  export declare const GPU_OVERRIDES: readonly ['webgpu', 'webgl', 'canvas2d', 'canvas'];
@@ -63,8 +64,8 @@ export type GpuOverride = (typeof GPU_OVERRIDES)[number];
63
64
  * `--renderer` flag), so validating here is validating once at the only place
64
65
  * an unchecked value enters. An unrecognized one clears the pin and says so:
65
66
  * before this, `?renderer=WebGL` or a typo was indistinguishable from passing
66
- * nothing at all, which is the one outcome a person debugging a renderer must
67
- * not silently get.
67
+ * nothing at all, and a person debugging a renderer got the default ladder
68
+ * with no sign the pin was ignored.
68
69
  */
69
70
  export declare function setGpuOverride(value: string | null): void;
70
71
  export declare function getGpuOverride(): string | null;
package/esm/gpuDevice.js CHANGED
@@ -11,13 +11,12 @@
11
11
  * button on the context-loss banner, which `packages/render-core/CLAUDE.md`
12
12
  * names as the only sanctioned fallback path once a backend is built.
13
13
  *
14
- * A statically-bundled plugin is the case this exists for. ADR-030 makes the
15
- * GPU surface static-import-only, so a third-party display carries its own copy
16
- * of this module by design; with module locals its `gpuOverride` would start
14
+ * A plugin that bundled render-core is the case this exists for. Every plugin
15
+ * built before ADR-128 did, so a third-party display can carry its own copy of
16
+ * this module; with module locals its `gpuOverride` would start
17
17
  * null and stay null, and the user who pins `?renderer=canvas2d` or clicks
18
18
  * "disable GPU" after a crash would watch the plugin ignore both and take
19
- * WebGPU anyway. Sharing the cell fixes that without putting anything in the
20
- * frozen `ReExports` ABI — which is the trade ADR-030 declined, and rightly.
19
+ * WebGPU anyway. Sharing the cell fixes that for every copy, served or bundled.
21
20
  *
22
21
  * **The shape is a cross-version contract.** Copies at different render-core
23
22
  * versions meet here, and whichever loads first wins the initialization, so a
@@ -188,10 +187,10 @@ async function createDevice() {
188
187
  * The renderers `?renderer=` can pin the page to. `canvas` is a spelling of
189
188
  * `canvas2d` kept because it is in the wild.
190
189
  *
191
- * A pin means *only* that rung — `createGpuHal` does not fall past it. That is
192
- * the whole point of the flag: it exists to compare backends, and a comparison
193
- * whose subject silently substitutes itself is worse than no comparison. The
194
- * failure is visible instead, as a `renderError` carrying the pin that could
190
+ * A pin means *only* that rung — `createGpuHal` does not fall past it. The flag
191
+ * is for comparing backends, and a comparison that renders with a different
192
+ * backend than the one pinned is worse than no comparison. The failure shows
193
+ * instead, as a `renderError` carrying the pin that could
195
194
  * not be honored, with the banner's "disable GPU" as the way out.
196
195
  */
197
196
  export const GPU_OVERRIDES = ['webgpu', 'webgl', 'canvas2d', 'canvas'];
@@ -207,8 +206,8 @@ function isGpuOverride(value) {
207
206
  * `--renderer` flag), so validating here is validating once at the only place
208
207
  * an unchecked value enters. An unrecognized one clears the pin and says so:
209
208
  * before this, `?renderer=WebGL` or a typo was indistinguishable from passing
210
- * nothing at all, which is the one outcome a person debugging a renderer must
211
- * not silently get.
209
+ * nothing at all, and a person debugging a renderer got the default ladder
210
+ * with no sign the pin was ignored.
212
211
  */
213
212
  export function setGpuOverride(value) {
214
213
  if (value !== null && !isGpuOverride(value)) {
@@ -1,8 +1,8 @@
1
1
  export interface GraphicsCapabilities {
2
2
  webgpu: boolean;
3
3
  /**
4
- * `undefined` when the WebGL2 probe was skipped, which is what
5
- * `getGraphicsCapabilities` does whenever WebGPU answered first — see there.
4
+ * `undefined` when the WebGL2 probe was skipped. `getGraphicsCapabilities`
5
+ * skips it whenever WebGPU answered first — see there.
6
6
  */
7
7
  webgl2?: boolean;
8
8
  gpuVendor?: string;
@@ -29,8 +29,8 @@ export interface GraphicsCapabilities {
29
29
  * The rungs of the rendering ladder, as the names a person reads — the About
30
30
  * widget's "Graphics:" line, the stack-trace dialog, the analytics field.
31
31
  *
32
- * Deliberately *not* the {@link GpuOverride} vocabulary, which is what a user
33
- * types into `?renderer=` and therefore carries lowercase spellings and the
32
+ * Deliberately *not* the {@link GpuOverride} vocabulary, the values a user
33
+ * types into `?renderer=`, which therefore carry lowercase spellings and the
34
34
  * `webgl`/`canvas` aliases. One is an input to parse, the other an answer to
35
35
  * display, and collapsing them would mean either showing "canvas2d" in the UI
36
36
  * or accepting "Canvas2D" in a URL.
@@ -77,9 +77,8 @@ export declare function getGraphicsCapabilities(): Promise<GraphicsCapabilities>
77
77
  * The Canvas2D pin is checked ahead of the record because the banner's
78
78
  * "disable GPU" sets it before the displays rebuild on the new rung. The other
79
79
  * pins win outright in the prediction, because **a pin never falls through to
80
- * the next rung** — `createGpuHal` throws instead, on the reasoning that a
81
- * pinned renderer which silently substitutes another makes any comparison
82
- * against it wrong. So the pinned rung *is* the answer, and a pin that cannot
80
+ * the next rung** — `createGpuHal` throws instead, because a pinned renderer
81
+ * that falls back to another rung makes any comparison against it wrong. So the pinned rung *is* the answer, and a pin that cannot
83
82
  * be honored surfaces as a `renderError` rather than as a different string
84
83
  * here. The one case the prediction cannot resolve from capabilities alone
85
84
  * (`?renderer=webgl` on a WebGPU machine, where the WebGL2 probe was skipped)
@@ -50,7 +50,7 @@ async function probeWebgpu() {
50
50
  * that call is effectively driver-wide (ADR-005 removed it from
51
51
  * `WebGL2Hal.dispose()` for the same reason), so probing while tracks were on
52
52
  * screen knocked out their live sibling contexts — and both browsers log the
53
- * loss to the console, which is what a user sees. The canvas is unreachable the
53
+ * loss to the console, where a user sees it. The canvas is unreachable the
54
54
  * moment this returns and the browser reclaims the context on GC, the same
55
55
  * release the HAL relies on. A page that then reaches the 16-context ceiling
56
56
  * evicts this one first (it is the oldest, and nothing draws to it or
@@ -123,9 +123,8 @@ export function getGraphicsCapabilities() {
123
123
  * The Canvas2D pin is checked ahead of the record because the banner's
124
124
  * "disable GPU" sets it before the displays rebuild on the new rung. The other
125
125
  * pins win outright in the prediction, because **a pin never falls through to
126
- * the next rung** — `createGpuHal` throws instead, on the reasoning that a
127
- * pinned renderer which silently substitutes another makes any comparison
128
- * against it wrong. So the pinned rung *is* the answer, and a pin that cannot
126
+ * the next rung** — `createGpuHal` throws instead, because a pinned renderer
127
+ * that falls back to another rung makes any comparison against it wrong. So the pinned rung *is* the answer, and a pin that cannot
129
128
  * be honored surfaces as a `renderError` rather than as a different string
130
129
  * here. The one case the prediction cannot resolve from capabilities alone
131
130
  * (`?renderer=webgl` on a WebGPU machine, where the WebGL2 probe was skipped)
@@ -91,7 +91,7 @@ async function climbLadder(canvas, { passes, sampleCount, failures }) {
91
91
  }
92
92
  }
93
93
  try {
94
- return new WebGL2Hal(canvas, passes);
94
+ return await WebGL2Hal.create(canvas, passes);
95
95
  }
96
96
  catch (e) {
97
97
  console.warn('[GPU] WebGL2 unavailable, falling back to Canvas2D:', e);
@@ -1,36 +1,31 @@
1
- import type { PipelineDescriptor, SampleCount } from './types.ts';
2
- /**
3
- * The two pipeline layouts every pass is built against. Both are created
4
- * eagerly now — the textured pair used to be lazy so that a display drawing no
5
- * textured pass paid nothing, which was worth two objects per *display* and is
6
- * not worth the null-bundling for two objects per *device*.
7
- */
8
- export interface DeviceLayouts {
9
- uniformOnlyBindGroupLayout: GPUBindGroupLayout;
10
- uniformOnlyPipelineLayout: GPUPipelineLayout;
11
- texturedBindGroupLayout: GPUBindGroupLayout;
12
- texturedPipelineLayout: GPUPipelineLayout;
1
+ import type { PipelineDescriptor, SampleCount, ShaderBinding } from './types.ts';
2
+ /** A pass's one bind-group layout, and the pipeline layout holding it as group 0. */
3
+ export interface PassLayout {
4
+ bindGroupLayout: GPUBindGroupLayout;
5
+ pipelineLayout: GPUPipelineLayout;
13
6
  }
14
7
  /**
15
- * The bind group and pipeline layouts for `device`, built once per device.
16
- *
17
- * A device that is lost drops out of the map with itself — `gpuDevice.ts`
18
- * releases its reference in the `.lost` handler and the next acquisition is a
19
- * new object, so the layouts and pipelines built against the dead one are
20
- * unreachable and there is no cache to invalidate by hand.
8
+ * A render pass's bind-group layout, read off its shader's reflected table:
9
+ * each binding visible to exactly the stages that read it, so the layout
10
+ * cannot disagree with the shader about which stage reads what. The uniform
11
+ * block takes a dynamic offset into the HAL's ring.
21
12
  */
22
- export declare function getDeviceLayouts(device: GPUDevice): DeviceLayouts;
13
+ export declare function bindGroupLayoutEntries(bindings: readonly ShaderBinding[]): GPUBindGroupLayoutEntry[];
23
14
  /**
24
- * The pipeline for `desc` at `sampleCount` on `device`, building it through
25
- * `build` on the first ask and handing every later one the same promise.
26
- *
27
- * `build` receives the sample count rather than closing over one, so the key
28
- * and the pipeline stored under it cannot disagree.
29
- */
30
- export declare function getOrBuildPipeline(device: GPUDevice, desc: PipelineDescriptor, sampleCount: SampleCount, build: (layouts: DeviceLayouts, sampleCount: SampleCount) => Promise<GPURenderPipeline>): Promise<GPURenderPipeline>;
31
- /**
32
- * Drop every device's entry. Tests only — nothing in the app invalidates this
33
- * cache, because losing the device is what invalidates it (see
34
- * {@link getDeviceLayouts}).
15
+ * What a pipeline is compiled from and cached under: `WebGPUHal` builds from
16
+ * nothing else. `uniformByteSize` stays out, since the layouts declare no
17
+ * `minBindingSize`, and so does the page-wide canvas format.
35
18
  */
19
+ export interface PipelineRecipe {
20
+ wgslSource: string;
21
+ bindGroupLayout: GPUBindGroupLayoutEntry[];
22
+ vertexBuffer: GPUVertexBufferLayout;
23
+ blend: GPUBlendState | undefined;
24
+ topology: GPUPrimitiveTopology;
25
+ /** Baked in: a pipeline built at 4 draws nothing into a target at 1. */
26
+ sampleCount: SampleCount;
27
+ }
28
+ export declare function pipelineRecipe(desc: PipelineDescriptor, wgslSource: string, sampleCount: SampleCount): PipelineRecipe;
29
+ export declare function getPassLayout(device: GPUDevice, entries: GPUBindGroupLayoutEntry[]): PassLayout;
30
+ export declare function getOrBuildPipeline(device: GPUDevice, recipe: PipelineRecipe, build: (recipe: PipelineRecipe) => Promise<GPURenderPipeline>): Promise<GPURenderPipeline>;
36
31
  export declare function resetDeviceGpuCacheForTests(device: GPUDevice): void;